大白话之 ts 的泛型
2023-07-07 00:24:12
# fontend
刚开始接触 ts 的时候,我以为泛型就是 any 类型,直到某天在使用时想如何才能让输入参数和输出的值类型一致,再回看指南文档,才发现这两个可是不一样呢。
any 类型其实就是不约束类型,写了就相当于没写。
但是泛型不一样,它的功能更强大。
1. 约束返回值的类型与传入参数的类型是相同的。
1 | // <T> 可以捕获用户的传入的类型 |
说明: 建议使用第2种方法,如果编译器不能自动推论,再使用第1种方法。
2. 在函数体中正确使用泛型变量
1 | function identity<T>(arg:T):T { |
泛型确实可以是任何类型或者自定义类型,但是如果没有明确定义的属性,在函数体中使用是会报错的,就像上面一样,此时该如何解决呢?
1 | // 1. |
如果要使用变量的相应属性,变量类型也必须能通过类型校验,否则编译阶段就会报错。
3. 除了可以定义泛型接口,还可以创建泛型类
注意: 泛型不能创建泛型枚举和泛型命名空间
1 | class GenericNumber<T> { |
将泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。类分两部分:静态部分和实例部分,泛型类指的是实例部分,静态属性不能使用泛型类型。
4. 泛型约束
比如上述第一个里面的泛型类型,想访问 .length 属性,那就必须要传入一个确定包含此属性的类型,就能允许访问,根据此需求可以列出对 T 的约束要求。在泛型中通过 extends 关键字实现约束:
1 | interface Lengthwise { |
现在这个泛型函数被定义了约束,所以参数不再是任意类型:
1 | identity(3) // Error, number doesn't have a .length property |
在泛型约束中,还可以使用类型参数:
声明一个类型参数,且它被另一个类型参数所约束。
1 | function getProperty<T, K extends keyof T>(obj: T, key: K) { |
5. 更高级的使用:在泛型中使用类类型
1 | // 创建工厂函数,引用构造函数的类类型 |