TypeScript学习笔记(六):泛型

时间:2022-04-05 16:03:05

认识泛型

TypeScript也实现了类型于C#和Java的泛型以实现类型的参数化,我们先看一个需求:

1 function identity(arg: any): any {
2 return arg;
3 }

我们希望方法identity可以传入任意类型,并且返回传入的类型,这样写可以达到效果但是不能确定返回的类型,使用泛型的写法如下:

TypeScript学习笔记(六):泛型
1 function identity<T>(arg: T): T {
2 return arg;
3 }
4
5 var output = identity<string>("myString"); // type of output will be 'string'
6 var output = identity("myString"); // type of output will be 'string'
TypeScript学习笔记(六):泛型

我们可以指定类型,也可以让编译器自动来识别类型。

泛型数组

我们也可以通过泛型来指定一个数组,写法如下:

TypeScript学习笔记(六):泛型
1 function loggingIdentity<T>(arg: T[]): T[] {
2 console.log(arg.length); // Array has a .length, so no more error
3 return arg;
4 }
5
6 function loggingIdentity<T>(arg: Array<T>): Array<T> {
7 console.log(arg.length); // Array has a .length, so no more error
8 return arg;
9 }
TypeScript学习笔记(六):泛型

泛型类型

我们可以指定一个带有泛型的函数:

1 function identity<T>(arg: T): T {
2 return arg;
3 }
4
5 var myIdentity: <U>(arg: U)=>U = identity;

还有另一种写法:

1 function identity<T>(arg: T): T {
2 return arg;
3 }
4
5 var myIdentity: {<T>(arg: T): T} = identity;

使用函数接口的写法如下:

TypeScript学习笔记(六):泛型
1 interface GenericIdentityFn {
2 <T>(arg: T): T;
3 }
4
5 function identity<T>(arg: T): T {
6 return arg;
7 }
8
9 var myIdentity: GenericIdentityFn = identity;
TypeScript学习笔记(六):泛型

同时泛型还可以作为类型的参数而不是方法的参数,写法如下:

TypeScript学习笔记(六):泛型
1 interface GenericIdentityFn<T> {
2 (arg: T): T;
3 }
4
5 function identity<T>(arg: T): T {
6 return arg;
7 }
8
9 var myIdentity: GenericIdentityFn<number> = identity;
TypeScript学习笔记(六):泛型

泛型类

泛型除了可以用在接口上以外,当然还可以用在类上:

TypeScript学习笔记(六):泛型
 1 class GenericNumber<T> {
2 zeroValue: T;
3 add: (x: T, y: T) => T;
4 }
5
6 var myGenericNumber = new GenericNumber<number>();
7 myGenericNumber.zeroValue = 0;
8 myGenericNumber.add = function(x, y) { return x + y; };
9
10 var stringNumeric = new GenericNumber<string>();
11 stringNumeric.zeroValue = "";
12 stringNumeric.add = function(x, y) { return x + y; };
13 alert(stringNumeric.add(stringNumeric.zeroValue, "test"));
TypeScript学习笔记(六):泛型

使用方法和C#与Java一致。

泛型约束

之前的泛型可以是任意的类型,我们还可以约束泛型的类型,我们先看一个会报错的例子:

1 function loggingIdentity<T>(arg: T): T {
2 console.log(arg.length); // Error: T doesn't have .length
3 return arg;
4 }

报错原因是,类型T没有length属性,我们可以为类型T指定一个类型,如下:

TypeScript学习笔记(六):泛型
1 interface Lengthwise {
2 length: number;
3 }
4
5 function loggingIdentity<T extends Lengthwise>(arg: T): T {
6 console.log(arg.length); // Now we know it has a .length property, so no more error
7 return arg;
8 }
TypeScript学习笔记(六):泛型

写法是通过extends来指定类型T的类型必须是实现了Lengthwise接口的类型。

调用如下:

1 loggingIdentity(3);  // Error, number doesn't have a .length property
2 loggingIdentity({length: 10, value: 3});

泛型约束泛型

某些情况下,我们可能会有如下的需求:

1 function find<T, U extends Findable<T>>(n: T, s: U) {   // errors because type parameter used in constraint
2 // ...
3 }
4 find (giraffe, myAnimals);

这种写法会报错,可以使用下面正确的写法来达到效果:

1 function find<T>(n: T, s: Findable<T>) {   
2 // ...
3 }
4 find(giraffe, myAnimals);

在泛型中使用类类型

有时我们希望可以指定泛型的构造函数和属性,写法如下:

1 function create<T>(c: {new(): T; }): T { 
2 return new c();
3 }

再看另外一个例子:

TypeScript学习笔记(六):泛型
 1 class BeeKeeper {
2 hasMask: boolean;
3 }
4
5 class ZooKeeper {
6 nametag: string;
7 }
8
9 class Animal {
10 numLegs: number;
11 }
12
13 class Bee extends Animal {
14 keeper: BeeKeeper;
15 }
16
17 class Lion extends Animal {
18 keeper: ZooKeeper;
19 }
20
21 function findKeeper<A extends Animal, K> (a: {new(): A;
22 prototype: {keeper: K}}): K {
23
24 return a.prototype.keeper;
25 }
26
27 findKeeper(Lion).nametag; // typechecks!
TypeScript学习笔记(六):泛型