文章目录
泛型是可以在保证类型安全前提下,让函数、类等与多种类型一起工作,从而灵活实现复用。常用于:函数、接口、class 中。
泛型的写法
变量名称的后面添加 < >(尖括号),尖括号中添加类型参数(类型变量)Type。该类型参数相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户使用该变量时指定)。这里的类型参数 Type,可以是任意合法的变量名称。
【函数的泛型写法】
// 使用泛型来创建一个函数:
function id<Type>(value: Type): Type {
return value
}
// 调用泛型函数:
const num = id<number>(10); // 以number类型调用,变量类型Type为number
const str = id<string>('a'); // 以string类型调用,变量类型Type为string
【类的泛型写法】
下面代码中传入了两个类型参数 <Type, vType> ,多个类型参数之间使用逗号( , )分隔。
// 定义泛型类
class C<Type, vType> {
value!: Type;
add!: (x: Type, y: Type) => vType;
}
// 实例化泛型类
let foo = new C<number, string>();
// 给类成员赋值
foo.value = 0;
foo.add = function (x, y) {
return x + "" + y;
};
【接口的泛型写法】
// 定义泛型接口
interface User<Type, nType> {
name: Type;
age: nType;
detail: () => Type;
}
// 使用
let box: User<string, number> = {
name: "小七",
age: 20,
detail() {
return this.name + this.age;
}
};
数组也是泛型接口
【类型别名的泛型写法】
// 定义类型别名
type Container<T> = { value: T };
// 使用
const a: Container<number> = { value: 0 };
案例:递归实现实现树形结构
// 定义树形结构
type Tree<T> = {
value: T;
left: Tree<T> | null;
right: Tree<T> | null;
};
泛型参数默认值
在 JS 中可以为参数设置默认值,TS 中同样也可以为泛型参数设置默认值,写法也与 JS 相同。
class Arr<Type = string> {
list: Type[] = [];
}
let arr = new Arr(); // 未传入类型参数,默认为string类型
arr.list = ['a','b']; // 正确
arr.list = [1,2,3,4]; // 报错
泛型参数约束
默认情况下,泛型的类型参数 Type 可以代表任意类型,这会导致变量无法访问任何属性。就需要为泛型添加约束来收缩类型(收缩类型取值范围)。
下面示例中,Type 可以代表任意类型,无法保证一定存在 length 属性,比如 number 类型。
function id<Type>(value: Type): Type {
console.log(value.length); // --报错
return value
}
方法一: 为类型参数指定更加具体的类型
下面示例中,将 value 的类型指定为 Type[] (Type 类型的数组),数组一定存在 length 属性,因此便可以访问 value.length 。
function id<Type>(value: Type[]): Type[] {
console.log(value.length); // 正确
return value
}
方法二:为泛型参数指定接口约束收缩类型
下面示例中,定义了接口 ILength ,要求必须提供 length 属性。泛型参数通过 extends 关键字使用该接口,为泛型的类型参数添加约束。·
interface ILength {
length: number
}
function id<Type extends ILength>(value: Type): Type {
console.log(value.length);
return value
}
id(['a', 'c']); // 传入数组 --正确
id('abc'); // 传入字符串 --正确
id({ length: 10, name: 'jack' }); // 传入对象,对象中有length属性 --正确
id(123); // 传入数字,没有length属性 -- 错误
该方法也可简写为
function id<Type extends { length: number }>(value: Type): Type {
console.log(value.length);
return value
}
【keyof 关键字】
keyof 关键字接收一个对象类型,生成由其属性名作为字面量类型组成的联合类型
下面示例中,先使用 typeof 关键字,获取 obj 对象的类型信息,再使用 keyof 关键字,获取 obj 对象的属性名,并生成联合类型。
let obj = { name: 'jack', age: 18, sex: 'male' };
let obj2: keyof typeof obj;
案例:向函数中传入两个参数,使第二个参数受第一个参数约束。
下面示例中,参数 Key extends keyof Type,使用 keyof 关键字,获取第一个参数 Type 对象中的属性名,并生成联合类型。再通过 extends 关键字使用联合类型作为第二个参数 Key 的约束条件。
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
console.log(obj[key]);
}
getProp({ name: 'jack', age: 18 }, 'age'); // 18
getProp({ name: 'jack', age: 18 }, 'name'); // jack
getProp(18, 'toFixed'); // ƒ toFixed() { [native code] }
getProp(['a', 'b'], 'length'); // 1
泛型类型工具
【Partial】
Partial<Type> 用来构造(创建)一个类型,将 Type 的所有属性设置为可选。
下面示例中,构造出来的新类型 PartialProps 结构和 Props 相同,但所有属性都变为可选的。
// 定义类型Props
type Props = {
id: string
children: number[]
}
// Partial<Type>
type PartialProps = Partial<Props>
let p1: Props = { id: '' }; // 缺少children属性 --报错
let p2: PartialProps = { id: '' }; // 所有属性均为可选属性 --正确
【Readonly】
Readonly<Type> 用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)。
下面示例中,构造出来的新类型 ReadonlyProps 结构和 Props 相同,但所有属性都变为只读的。
// 定义类型Props
type Props = {
id: string
age: number
}
// Readonly<Type>
type PartialProps = Readonly<Props>
let p1: Props = { id: 'a', age: 18 };
p1.age = 20; // 正常使用
let p2: PartialProps = { id: 'b', age: 18 };
p2.age = 20; // 所有属性均为只读属性 --报错
【Pick】
Pick<Type, Keys> 从 Type 中选择一组属性来构造新类型。
Pick 工具类型有两个类型参数:1 表示选择谁的属性 2 表示选择哪几个属性。
第二个类型参数 Keys 传入的属性只能是第一个类型参数中存在的属性。
下面示例中,构造出来的新类型 PickProps,只有 id 和 title 两个属性类型。
// 定义类型Props
type Props = {
id: number
name: string
age: number
}
// Pick<Type, Keys>
type PartialProps = Pick<Props, 'age' | 'name'>
let p1: Props = { id: 1, name: 'a', age: 18 };
p1.age = 20; // 正常使用
let p2: PartialProps = { id: 2, name: 'b', age: 18 };
p2.age = 20; // PartialProps中只有name和age属性 --报错
【Record】
Record<Keys,Type> 构造一个对象类型,属性键为 Keys,属性类型为 Type。
Record 工具类型有两个类型变量:1 表示对象有哪些属性 2 表示对象属性的类型。
场景:创建有多个属性的对象,且这些属性均为同一类型。
下面示例中,构建的新对象类型 RecordObj 表示:这个对象有三个属性分别为a/b/c,属性值的类型都是 string。
// Record<Keys,Type>
type RecordObj = Record<'a' | 'b' | 'c', string>
// 等价于
// type RecordObj = {
// a: string
// b: string
// c: string
// }
let obj: RecordObj = {
a: 'a',
b: 'b',
c: 'c'
}
更多泛型类型工具https://wangdoc.com/typescript/utility#returntypetype