泛型简单来说就是类型变量,在 ts 中存在类型,如 number、string、boolean等。泛型就是使用一个类型变量来表示一种类型,类型值通常是在使用的时候才会设置。泛型的使用场景非常多,可以在函数、类、interface 接口中使用
TypeScript中不建议使用 any 类型,不能保证类型安全,调试时缺乏完整的信息。
TypeScript 可以使用泛型来创建可重用的组件。支持当前数据类型,同时也能支持未来的数据类型。扩展灵活,可以在编译时发现类型错误,从而保证了类型安全。
无泛型用法
//数字类型
functionnum(A:number,B:number):Array<number>{//Array<number>为希望返回number类型的数组
return [a,b]
}
num(6,9)
//字符串类型
functionstr(A:string,B:string):Array<string>{//Array<number>为希望返回number类型的数组
return [a,b]
}
str('小满','穿女装')
一个笨的方法就像上面那样,也就是说 JS 提供多少种类型,就需要复制多少份代码,然后改下类型签名。这对程序员来说是致命的。这种复制粘贴增加了出错的概率,使得代码难以维护,牵一发而动全身。并且将来 JS 新增新的类型,你仍然需要修改代码,也就是说你的代码对修改开放,这样不好。
如果你使用 any 的话,怎么写都是 ok 的, 这就丧失了类型检查的效果。实际上我知道我传给你的是 string,返回来的也一定是 string,而 string 上没有 toFixed 方法,因此需要报错才是我想要的。也就是说我真正想要的效果是:
当我用到id的时候,你根据我传给你的类型进行推导
。比如我传入的是 string,但是使用了 number 上的方法,你就应该报错。
使用泛型优化
为了解决上面的这些问题,我们使用泛型对上面的代码进行重构。和我们的定义不同,这里用了一个 类型 T,这个 T 是一个抽象类型,只有在调用的时候才确定它的值,这就不用我们复制粘贴无数份代码了。
其中
T
代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上T
可以用任何有效名称代替。除了T
之外,以下是常见泛型变量代表的意思:
- K(Key):表示对象中的键类型;
- V(Value):表示对象中的值类型;
- E(Element):表示元素类型。
functionadd<T>(a:T,b:T):Array<T>{//通常定义的时候类型是不明确的,所以一般使用T来定义
return [a,b];
}
add<number>(1,2)//1对应a,2对应b、返回的都是number类型
add<string>('1','2')//这个时候,我们只需要改动这个string,传递到上面的时候就会自动推断为string类型了
//甚至我们可以简写
add(1,2)
add('1','2')//编辑器会自动推断类型,但最好还是写一下,如果你知道你具体需要的是什么的话
//对泛型进行总结就是:定义前不明确类型,使用的时候再明确类型,能够给我们保留有足够的自由度,又不会像any丧失类型检查的效果
我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。
functionSub<T,U>(a:T,b:U):Array<T|U> {//这个T跟U随便起名字都行,没有强制规范
constparams:Array<T|U>= [a,b]
returnparams
}
Sub<Boolean,number>(false,1)//我们这里就将其定义为布尔值类型跟数字类型
定义泛型接口
声明接口的时候 在名字后面加一个 <参数>
使用的时候传递类型
interfaceMyInter<T> {
(arg: T): T
}
functionfn<T>(arg: T): T {
returnarg
}
letresult: MyInter<number>=fn
result(123)
对象字面量泛型
letfoo: { <T>(arg: T): T }
foo=function<T>(arg:T):T {
returnarg
}
foo(123)
泛型约束(函数类)
我们期望在一个泛型的变量上面,获取其
length
参数,但是,有的数据类型是没有length
属性的
functiongetLegnth<T>(arg:T) {
returnarg.length
}
- 这个时候,我们就可以对其进行约束
interfaceLen{
length:number
}
functiongetLegnth<TextendsLen>(arg:T) {//使用接口让泛型T继承了Len
returnarg.length
}
getLength(1)//这个时候我们这样使用就会提示我们类型"number"的参数不能赋给"Len"的参数
//我们依次对数组、字符串、布尔值都进行尝试,分别为可以、可以、不可以