理解TypeScript的泛型

可以使用泛型來創(chuàng)建可重用的組件,一個組件可以支持多種類型的數(shù)據(jù)。這樣用戶就可以以自己的數(shù)據(jù)類型來使用組件。
泛型函數(shù)
// arg 參數(shù)類型是一個泛型變量 T,也可以使用不同的泛型參數(shù)名function identity<T>(arg: T): T {return arg;}
們定義了泛型函數(shù)后,可以用兩種方法使用。第一種是,傳入所有的參數(shù),包含類型參數(shù):
let output = identity<string>("myString"); // type of output will be 'string'這里我們明確的指定了 T 是 string 類型,并做為一個參數(shù)傳給函數(shù),使用了<> 括起來而不是()。
第二種方法更普遍。利用了類型推論 -- 即編譯器會根據(jù)傳入的參數(shù)自動地幫助我們確定T的類型:
let output = identity("myString"); // type of output will be 'string'注意我們沒必要使用尖括號(<>)來明確地傳入類型;編譯器可以查看 myString 的值,然后把 T 設(shè)置為它的類型。類型推論幫助我們保持代碼精簡和高可讀性。如果編譯器不能夠自動地推斷出類型的話,只能像上面那樣明確的傳入 T 的類型,在一些復(fù)雜的情況下,這是可能出現(xiàn)的。
泛型函數(shù)的類型與非泛型函數(shù)的類型沒什么不同,只是有一個類型參數(shù)在最前面。
泛型接口
接口能夠描述 JavaScript 中對象擁有的各種各樣的外形。除了描述帶有屬性的普通對象外,接口也可以描述函數(shù)類型。
為了使用接口表示函數(shù)類型,我們需要給接口定義一個調(diào)用簽名。它就像是一個只有參數(shù)列表和返回值類型的函數(shù)定義。參數(shù)列表里的每個參數(shù)都需要名字和類型。
interface GenericIdentityFn {<T>(arg: T): T;}function identity<T>(arg: T): T {return arg;}let myIdentity: GenericIdentityFn = identity;
一個相似的例子,我們可能想把泛型參數(shù)當作整個接口的一個參數(shù)。這樣我們就能清楚的知道使用的具體是哪個泛型類型。這樣接口里的其它成員也能知道這個參數(shù)的類型了。
interface GenericIdentityFn<T> {(arg: T): T;}function identity<T>(arg: T): T {return arg;}let myIdentity: GenericIdentityFn<number> = identity;
泛型類
泛型類看上去與泛型接口差不多。泛型類使用( <>)括起泛型類型,跟在類名后面。
class GenericNumber<T> {zeroValue: T;add: (x: T, y: T) => T;}let myGenericNumber = new GenericNumber<number>();
泛型約束
我們有時候想操作某類型的一組值,并且我們知道這組值具有什么樣的屬性。在 loggingIdentity 例子中,我們想訪問 arg 的 length 屬性,但是編譯器并不能證明每種類型都有 length 屬性,所以就報錯了。
function loggingIdentity<T>(arg: T): T {console.log(arg.length); // Error: T doesn't have .lengthreturn arg;}
相比于操作 any 所有類型,我們想要限制函數(shù)去處理任意帶有 .length 屬性的所有類型。只要傳入的類型有這個屬性,我們就允許,就是說至少包含這一屬性。為此,我們需要列出對于T的約束要求。
為此,我們定義一個接口來描述約束條件。創(chuàng)建一個包含 .length 屬性的接口,使用這個接口和 extends 關(guān)鍵字來實現(xiàn)約束:
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 errorreturn arg;}
