泛型类型和函数
泛型类型和函数使代码能够以类型安全的方式操作多种数据类型,而无需为每种类型编写重复的逻辑。
泛型类和接口
类和接口可以定义为泛型,将参数添加到类型定义中。如以下示例中的类型参数Element:
class CustomStack<Element> {
public push(e: Element):void {
// ...
}
}
要使用类型CustomStack,必须为每个类型参数指定类型实参:
let s = new CustomStack<string>();
s.push('hello');
编译器在使用泛型类型和函数时会确保类型安全。参见以下示例:
let s = new CustomStack<string>();
s.push(55); // 将会产生编译时错误
泛型约束
泛型类型的类型参数可以被限制只能取某些特定的值。例如,MyHashMap<Key, Value>这个类中的Key类型参数必须具有hash方法。
interface Hashable {
hash(): number;
}
class MyHashMap<Key extends Hashable, Value> {
public set(k: Key, v: Value) {
let h = k.hash();
// ...其他代码...
}
}
在上面的例子中,Key类型扩展了Hashable,Hashable接口的所有方法都可以为key调用。
泛型函数
使用泛型函数可编写更通用的代码。比如返回数组最后一个元素的函数:
function last(x: number[]): number {
return x[x.length - 1];
}
last([1, 2, 3]); // 3
如果需要为任何数组定义相同的函数,使用类型参数将该函数定义为泛型:
function last<T>(x: T[]): T {
return x[x.length - 1];
}
现在,该函数可以与任何数组一起使用。
在函数调用中,类型实参可以显式或隐式设置:
// 显式设置的类型实参
let res: string = last<string>(['aa', 'bb']);
let res: number = last<number>([1, 2, 3]);
// 隐式设置的类型实参
// 编译器根据调用参数的类型来确定类型实参
let res: number = last([1, 2, 3]);
泛型默认值
泛型类型的类型参数可以设置默认值,这样无需指定实际类型实参,直接使用泛型类型名称即可。以下示例展示了类和函数的这一特性。
class SomeType {}
interface Interface <T1 = SomeType> { }
class Base <T2 = SomeType> { }
class Derived1 extends Base implements Interface { }
// Derived1在语义上等价于Derived2
class Derived2 extends Base<SomeType> implements Interface<SomeType> { }
function foo<T = number>(): void {
// ...
}
foo();
// 此函数在语义上等价于下面的调用
foo<number>();