本文搬运于TypeScript中文网:https://www.tslang.cn/docs/handbook/generics.html
目录
一个例子!!
创建一个identity函数:这个函数会返回任何传入它的值。
不用泛型:
function identity(arg: number): number {
return arg;
}
或者,使用any
类型来定义函数:
function identity(arg: any): any {
return arg;
}
那么,使用any
类型会导致这个函数可以接收任何类型的arg
参数。
泛型变量
对于上面的例子,我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量,一种特殊的变量,只用于表示类型而不是值。
用了泛型:
function identity<T>(arg: T): T {
return arg;
}
T
帮助我们捕获用户传入的类型(比如:number
),之后我们就可以使用这个类型。 之后我们再次使用了 T
当做返回值类型。
一个问题,报错了!
function loggingIdentity<T>(arg: T): T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}
注:这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有 .length
属性的。
我们可以这样:
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
泛型约束
还可以怎么解决上面那个问题呢,那么,我们定义一个接口来描述约束条件。 创建一个包含 .length
属性的接口,使用这个接口和extends
关键字来实现约束:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
继而,现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
loggingIdentity(3); // Error, number doesn't have a .length property
我们需要传入符合约束类型的值,必须包含必须的属性:
loggingIdentity({length: 10, value: 3});
使用泛型函数
第一种:传入所有的参数,包含类型参数
let output = identity<string>("myString"); // type of output will be 'string'
这里我们明确的指定了 T
是string
类型,并做为一个参数传给函数,使用了<>
括起来。
第二种:利用了类型推论 -- 即编译器会根据传入的参数自动地帮助我们确定T的类型
let output = identity("myString"); // type of output will be 'string'
泛型函数的类型与非泛型函数的类型没什么不同,只是有一个类型参数在最前面
function identity<T>(arg: T): T {
return arg;
}
//也可以使用不同的泛型参数名 T-->U
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;
// 我们可能想把泛型参数当作整个接口的一个参数。 这样我们就能清楚的知道使用的具体是哪个泛型类型
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; };
与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。