必须谨慎使用类型变量,参考下面的例子:
// 定义泛型函数
function getLength<T>(arg: T): number {
// 错误,编译器不知道类型变量T是否包含属性length,默认为不存在
// error TS2339: Property 'length' does not exist on type 'T'
return arg.length;
}
上例中没有任何地方表明 T
类型的参数 arg
包含属性 length
,因而编译器认为不存在属性 length
报错。为了改正上述代码,需要约束类型变量 T
必须包含属性 length
类型变量 extends 类型
类型约束关键字为 extends
,和继承关键字一样。实际上,类型约束跟继承同义,类型变量继承了被约束类型的所有成员
// 声明接口
interface WithLength {
length: number;
}
// 正确,T现在被接口类型WithLength约束,包含属性 length
function getLength<T extends WithLength>(arg: T): number {
return arg.length;
}
参考下面的例子:
interface MyType<T> {
value: T;
}
// error TS2314: Generic type 'MyType<T>' requires 1 type argument(s)
let a: MyType = {
value: "hello world"
}
泛型在使用的时候必须用实际类型取代类型参数,假如同上面那样,编译器就无法判定a.value
的实际类型是什么(有可能是任何类型),因而报错。修正这个错误很简单,只需要在使用的时候传递类型参数即可:
// 正确
let a: MyType<string> = {
value: "hello world"
}
每次都用尖括号语法注解确实挺烦的,尤其是在有些情况下,其他人使用 MyType
接口的时候可能并不知道这是一个泛型接口声明。TypeScript提供了默认类型语法:
类型变量 = 类型
参考下面的例子:
interface MyType<T = string> {
value: T;
}
// 正确,在类型参数没有显示指定的情况下,采用了默认类型 string
let x1: MyType = {
value: "hello world"
}
// 等价于
let x1: MyType<string> = {
value: "hello world"
}
// 错误, error TS2322: Type 'number' is not assignable to type 'string'
let x2: MyType = {
value: 123
}