一、TypeScript-泛型
1. 泛型 【generics】
-
什么是泛型
- 泛指的类型,是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
-
应用场景:
- 一个方法或者一个组件需要支持多种数据的类型,用户可以根据自己传入的数据类型来使用该方法或者组件。
-
为什么不使用any类型?
- any类型是接受任何类型的arg参数,传入值的类型和返回的值的类型只是可能相同,存在不确定性。
2. 泛型的定义和使用
- 使用泛型可以创建泛型变量、泛型函数、泛型接口、泛型类,不可以创建泛型枚举和泛型命名空间
-
创建泛型变量,表示这些参数是任意或者所有类型,只有使用的时候,才能被确定是什么类型。
// first: T 和 second: T 就是一个泛型变量 function join<T> (first: T, second: T) { return `${first} + ${second}`; } // User类中的firstName: T; 就是一个泛型变量 class User<T>{ firstName: T; } // Resp接口中的code: T;就是一个泛型变量 interface Resp<T> { code: T; }
-
创建泛型函数
/** * 根据输入的长度和输入的值创建一个数组 * @param length 输入的长度 * @param value 输入的值 * @returns Array<T> 输入类型的数组 */ function createArray<T>(length: number, value: T): Array<T> { let result: T[] = []; for (let i = 0; i < length; i++) { result[i] = value; } return result; } // 调用指定泛型 createArray<string>(4, "a"); // 根据ts的推断机制去推断泛型 createArray(4, "a"); // 多个泛型 function connect<T, U>(first: T, second: U) { return `${first} + ${second}`; }
- 函数名后添加,其中T用来表示任意输入类型。
- 只有函数名添加了,在行参、返回值和函数体中才可以使用该泛型。
- 再调用时指定泛型
- 也可以让ts的推断机制去推断泛型,保证了代码精简和高可读性,但是在一些复杂的情况下,可能推断不出。
- 当然也可以定义多个泛型
-
创建泛型接口
// 泛型接口 interface ConfigFn<T> { (value: T): T; } function getData<T>(value: T): T { return value; } let myGetData: ConfigFn<string> = getData; myGetData("20");
-
创建泛型类
class DataManager<T> { constructor(private data: T[]) {} getItem(index: number): T { return this.data[index]; } } const data = new DataManager<string>(["1"]); data.getItem(0);
- 类有两个部分:静态部分和实例部分。泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。
-
3. 泛型约束
-
,通过接口约束,关键字:extends
interface Item { name: string; } // T泛型继承Item,表示后期使用的时候必须包含name属性 class DataManager<T extends Item> { constructor(private data: T[]) {} getItem(index: number): string { return this.data[index].name; } } const data = new DataManager([{ name: "zhangsan" }]); data.getItem(0);
-
在泛型约束中使用类型参数
-
声明一个类型参数,且它被另一个类型参数所约束
function getProperty(obj: T, key: K) { return obj[key]; } let x = { a: 1, b: 2, c: 3, d: 4 }; getProperty(x, "a"); // 编译正常 getProperty(x, "m"); // 编译报错
- 用属性名从对象里获取这个属性值。 并且我们想要确保这个属性存在于对象
obj
上,因此需要在这两个类型之间使用约束。
- 用属性名从对象里获取这个属性值。 并且我们想要确保这个属性存在于对象
-
-
在泛型里使用类类型
// 在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型 function create<T>(c: {new(): T; }): T { return new c(); } // 使用原型属性推断并约束构造函数与类实例的关系 class BeeKeeper { hasMask: boolean; } class ZooKeeper { nametag: string; } class Animal { numLegs: number; } class Bee extends Animal { keeper: BeeKeeper; } class Lion extends Animal { keeper: ZooKeeper; } function createInstance<A extends Animal>(c: new () => A): A { return new c(); } createInstance(Lion).keeper.nametag; createInstance(Bee).keeper.hasMask;
4. 改进
-
在 TypeScript 2.3 以后,可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。
function join<T = number>(first: T, second: T) { return `${first} + ${second}`; }