being able to create a component that can work over a variety of types rather than a single one
还是为了复用
Hello World of Generics
function identity<T>(arg: T): T {
return arg;
}
// 可能会想使用 any, 但是使用 any 就会丢失了类型信息
function identity(arg: any): any {
return arg;
}
// type of output will be 'string'
// explicity set T
let output = identity<string>("myString");
// or ts automatically inference it
let output = identity("myString");
Working with Generic Type Variables
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // Array has a .length, so no more error
return arg;
}
Generic Types
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
// 不一定要用 T, 只要不冲突随便用, 只是约定俗成用 T
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <U>(arg: U) => U = identity;
// 使用带有 call signature 的对象字面量来定义泛型函数
// 由此引出下面的带有泛型函数的接口
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 GernericIndentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GernericIndentityFn<number> = identity;
Generic Classes
就和泛型接口很像, 类型后面加上
注意只能用在类的 instance side
Generic classes are only generic over their instance side
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
// with number
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
// with string
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };
Generic Constraints
为 T 定义一个约束的集合
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;
}
用一个类型参数来约束另一个类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // okay
getProperty(x, "m"); // error: Argument of type 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'.
using class types in generics
// 工厂函数
function create<T>(c: {new(): T; }): T {
return new c();
}
更高级的运用, 构造函数和实例间的约束
class BeeKeeper {
hasMask: boolean;
}
class ZooKeeper {
nametag: string;
}
class Animal {
numLegs: number;
}
class Bee extends Animal {
keeper: BeeKeeper;
}
class Lion extends Animal {
keeper: ZooKeeper;
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag; // typechecks!
createInstance(Bee).keeper.hasMask; // typechecks!