软件工程主要部分就是构建不仅良好定义,通用的而且可复用APIs。
type variable
能确定传入参数的类型也能用也能用来表示返回的类型
function identity<T>(arg: T): T {
return arg;
}
上面的就叫做Generics
,它允许我们在函数的一侧导入Type information
,然后在另一侧输出这个Type information
泛型函数调用
- 显示传入类型参数
let output = identity<string>("myString"); // type of output will be 'string'
- 通过TypeScript的
type infer
自动帮我们判断并设置Type T
let output = identity("myString"); // type of output will be 'string'
- 在更复杂的用法中,可能需要显示的传入类型参数
泛型类型Generic Types
为了表示一下泛型函数的类型:
function identity<T>(arg: T): T {
return arg;
}
我们可以定义一下几种方式:
- 直接写成函数类型
let myIdentity: <T>(arg: T) => T = identity;
T的命名方式随意,只要数量和和使用方式相同就
let myIdentity: <U>(arg: U) => U = identity;
- 写在对象字面量的
call signature
里面
let myIdentity: {<T>(arg: T): T} = identity;
- 写在
Interface
里面,定义generic interface
interface GenericIdentityFn {
<T>(arg: T): T;
}
let myIdentity: GenericIdentityFn = identity;
我们也可以创建Generic class,但是不能创建Generic enums 和 Generic namespaces
Generic Constraints
- 我们想要创建一个能处理类型集的泛型函数,而且我们知道这个类型集有哪些方法,能做什么。那么我们就需要对泛型做一些限制来保证这些类型集的方法可用。
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'.
- 在泛型中使用
class types
- 用构造函数来引用类的类型
function create<T>(c: {new(): T; }): T {
return new c();
}
- 使用原型属性来推断和限制类类型的
constructor side
和instance side
的关系
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!