認識泛型
TypeScript也實現了類型於C#和Java的泛型以實現類型的參數化,我們先看一個需求:
1 function identity(arg: any): any { 2 return arg; 3 }
我們希望方法identity可以傳入任意類型,並且返回傳入的類型,這樣寫可以達到效果但是不能確定返回的類型,使用泛型的寫法如下:
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'
我們可以指定類型,也可以讓編譯器自動來識別類型。
泛型數組
我們也可以通過泛型來指定一個數組,寫法如下:
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 }
泛型類型
我們可以指定一個帶有泛型的函數:
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;
使用函數接口的寫法如下:
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;
同時泛型還可以作為類型的參數而不是方法的參數,寫法如下:
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;
泛型類
泛型除了可以用在接口上以外,當然還可以用在類上:
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"));
使用方法和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指定一個類型,如下:
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 }
寫法是通過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 }
再看另外一個例子:
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!