泛型
什么是泛型?
我们在编写程序时,经常遇到两个模块的功能非常相似,只是一个是处理 int 数据,另一个是处理 string 数据,或者其他自定义的数据类型,但我们没有办法,只能分别写多个方法处理每个数据类型,因为方法的参数类型不同。有没有一种办法,在方法中传入通用的数据类型,这样不就可以合并代码了吗?泛型的出现就是专门解决这个问题的。
组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。可以使用泛型
来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
当不使用泛型的时候
function identity(arg: number): number {
return arg
}
function identity1(arg: string): string {
return arg
}
function identity(arg: any): any {
return arg
}
这样的两个函数的结构是一样的只是因为参数不一样,才导致需要写两个不同的函数,也许会想到用 any
来实现这样的效果,但是如果使用any
的话,我们就无法确定值的类型是什么了,会丢失一些信息:传入的类型与返回的类型应该是相同的。
所以就有了下面这种写法
function identity<T>(arg: T): T {
return arg
}
我们使用一个一个类型变量T
来接受变量的类型 ,如果参数是 number 类型的数据,则 T 的值就是 number。上面的这种形式的函数就叫做 泛型
,它可以适用于多个类型。
泛型的使用
第一种,传入所有的参数,包含类型参数,下面的方法中使用<>
明确的指定了T
是string
类型的
let output = identity < string > "hello world"
第二种,使用类型推断
编译器会自动根据我们传入的参数来确定 T 的类型 (推荐使用)
let output = identity("hello world")
这样的使用方式就没必要使用尖括号(<>
)来手动传入具体的类型,编译器会根据我们传入的值的类型来设置 T 的值
function identity<T>(arg: T): T {
console.log(arg.length) // 类型“T”上不存在属性“length”
return arg
}
因为类型变量代表的是任意类型,所以可以是 number 或者 Array 等,而数字是没有.length
属性的。
要想实现上面的,可以如下方式来写
function identity<T>(arg: Array<T>): Array<T> {
console.log(arg.length) // Array has a .length, so no more error
return arg
}
identity 接收类型参数T
和参数arg
,它是个元素类型是T
的数组,并返回元素类型是T
的数组。 如果我们传入数字数组,将返回一个数字数组,因为此时 T
的的类型为number
。 这可以让我们把泛型变量 T 当做类型的一部分使用,而不是整个类型,增加了灵活性。
泛型类型
泛型类型的函数和非泛型函数的类型没什么不同,只是有一个类型参数在前面
function identity<T>(arg: T): T {
return arg
}
let myIdentity: <T>(arg: T) => T = identity
// 使用不同的泛型参数名 ,只要在数量上和使用方式上能对应上就可以
function identity<T>(arg: T): T {
return arg
}
let myIdentity: <U>(arg: U) => U = identity
// 使用带有调用签名的对象字面量来定义泛型函数
function identity<T>(arg: T): T {
return arg
}
let myIdentity: { <T>(arg: T): T } = identity
用上面的的对象字面量拿出来作为接口
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg
}
let myIdentity: GenericIdentityFn = identity
我们也可以把泛型参数当作整个接口的一个参数,因此可以如下修改代码
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg
}
let myIdentity: GenericIdentityFn<number> = identity
泛型类
泛型类使用( <>
)括起泛型类型,跟在类名后面
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
泛型约束
上面我们在使用 .length
的时候报错了,我们可以增加一个约束来实现这个。
interface Lengthwise {
length: number;
}
function identity<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg;
}
增加了约束后,我们调用的方式也得做修改
// 这样是不行的
identity(2) // Error, number doesn't have a .length property
需要传入带约束的值
identity({ length: 2, value: 3 })