TypeScript 泛型的作用是什么
工具 使用时指定类型
TypeScript 泛型是一种工具。它能让开发者不在定义时指定类型,而在使用时指定类型。
类型参数在类名后面的尖括号中指定。泛型类可以具有泛型字段或方法。
1class HZFEMember<T, U> {
2 private id!: T;
3 private name!: U;
4
5 setMember(id: T, name: U): void {
6 this.id = id;
7 this.name = name;
8 }
9
10 show(): void {
11 console.log(`ID: ${this.id}, Name: ${this.name}`);
12 }
13}
14
15const member1 = new HZFEMember<number, string>();
16member1.setMember(1, "QingZhen");
17member1.show(); // ID: 1, Name: QingZhen
18
19const member2 = new HZFEMember<string, string>();
20member2.setMember("02", "Aki");
21member2.show(); // ID: 02, Name: Aki1interface HZFEMember<T, U> {
2 id: T;
3 name: U;
4}
5
6const member1: HZFEMember<number, string> = {
7 id: 1,
8 name: "QingZhen",
9};
10console.log(`ID: ${member1.id}, Name: ${member1.name}`); // ID: 1, Name: QingZhen
11
12const member2: HZFEMember<string, string> = {
13 id: "02",
14 name: "Aki",
15};
16console.log(`ID: ${member2.id}, Name: ${member2.name}`); // ID: 02, Name: Aki函数类型的泛型接口
1interface ShowHZFEMember<T, U> {
2 (id: T, name: U): void;
3}
4
5const showHZFEMember: ShowHZFEMember<number, string> = function (id, name) {
6 console.log(`ID: ${id}, Name: ${name}`);
7};
8showHZFEMember(1, "QingZhen"); // ID: 1, Name: QingZhen
9
10const showHZFEMember2: ShowHZFEMember<string, string> = function (id, name) {
11 console.log(`ID: ${id}, Name: ${name}`);
12};
13showHZFEMember2("02", "Aki"); // ID: 02, Name: Aki在下面的例子中访问 member 的 id 属性时,因为编译器并不能证明 member 中有 id 属性,所以会报错。
1function getHZFEMember<T>(member: T): T {
2 console.log(`ID: ${member.id}`); // Property 'id' does not exist on type 'T'.
3 return member;
4}如果我们想要限制函数只能处理带有 id 属性的类型,就需要列出对于 T 的约束要求。我们可以定义一个接口来描述约束条件,创建一个包含 id 属性的接口,使用这个接口和 extends 关键字来实现约束。
1interface Member {
2 id: number;
3}
4
5function getHZFEMember<T extends Member>(member: T): T {
6 console.log(`ID: ${member.id}`);
7 return member;
8}
9
10getHZFEMember("QingZhen"); // Argument of type 'string' is not assignable to parameter of type 'Member'.
11getHZFEMember({ id: 1, name: "QingZhen" }); // ID: 1TypeScript 提供了一些内置的工具类型,本质上也是通过范型来实现的。下面,我们通过几种常用的类型来看看它们是怎么实现的。
通过将 Type 中的所有属性都设置为可选来构造一个新的类型。
1interface Member {
2 id: number;
3 name: string;
4 age: number;
5}
6const hzfer: Member = {
7 id: 1,
8 name: "Qingzhen",
9}; // Property 'age' is missing in type '{ id: number; name: string; }' but required in type 'Member'.
10
11type HZFEMember = Partial<Member>;
12const hzfer2: HZFEMember = {
13 id: 1,
14 name: "Qingzhen",
15}; // No errors源码:
1type Partial<T> = {
2 [P in keyof T]?: T[P];
3};通过将 Type 中的所有属性都设置为必选来构造一个新的类型。和 Partial 相反。
1interface Member {
2 id: number;
3 name: string;
4 age?: number;
5}
6const hzfer: Member = {
7 id: 1,
8 name: "Qingzhen",
9}; // No errors
10
11type HZFEMember = Required<Member>;
12const hzfer2: HZFEMember = {
13 id: 1,
14 name: "Qingzhen",
15}; // Property 'age' is missing in type '{ id: number; name: string; }' but required in type 'Required<Member>'源码:
1type Required<T> = {
2 [P in keyof T]-?: T[P];
3};从联合类型 UnionType 中排除 ExcludedMembers 中的所有联合成员来构造一个新的类型。
1type HZFEMemberProps = Exclude<"id" | "name" | "age", "age">;
2
3const hzferProp: HZFEMemberProps = "age"; // Type '"age"' is not assignable to type 'HZFEMemberProps'.源码:
1type Exclude<T, U> = T extends U ? never : T;从一个已有的类型 Type 中选择一组属性 Keys 来构造一个新的类型。
1interface Member {
2 id: number;
3 name: string;
4 age: number;
5}
6
7type HZFEMember = Pick<Member, "id" | "name">;
8
9const hzfer: HZFEMember = {
10 id: 1,
11 name: "QingZhen",
12 age: 18, // Object literal may only specify known properties, and 'age' does not exist in type 'HZFEMember'.
13};源码:
1type Pick<T, K extends keyof T> = {
2 [P in K]: T[P];
3};从一个已有的类型 Type 中移除一组属性 Keys 来构造一个新的类型。
1interface Member {
2 id: number;
3 name: string;
4 age: number;
5}
6
7type HZFEMember = Omit<Member, "id" | "age">;
8
9const hzfer: HZFEMember = {
10 id: 1, // Object literal may only specify known properties, and 'id' does not exist in type 'HZFEMember'.
11 name: "QingZhen",
12};源码:
1type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;构造一个由函数的返回值的类型 Type 组成的类型。
1interface GetHZFEMember {
2 (id: number): {
3 id: number;
4 name: string;
5 age: number;
6 };
7}
8
9type HZFEMember = ReturnType<GetHZFEMember>; // type HZFEMember = { id: number; name: string; age: number; };源码:
1type ReturnType<T extends (...args: any) => any> = T extends (
2 ...args: any
3) => infer R
4 ? R
5 : any;infer 表示在 extends 条件语句中待推断的类型变量。
上文的 ReturnType 源码中的 infer R 就代表了待推断的函数的返回值类型。
借助这个能力,我们可以通过 infer 来实现 tuple 转 union:
1// 如果泛型参数 T 满足约束条件 Array<infer I>,那么就返回这个类型变量
2type TypeOfArrayItem<T> = T extends Array<infer I> ? I : never;
3
4type MyTuple = [string, number];
5
6type MyUnion = TypeOfArrayItem<MyTuple>; // string | number