泛型
軟件工程中,我們不僅要創建一致的定義良好的API,同時也要考慮可重用性。 組件不僅能夠支持當前的數據類型,同時也能支持未來的數據類型,這在創建大型系統時為你提供了十分靈活的功能。
在像C#和Java這樣的語言中,可以使用泛型來創建可重用的組件,一個組件可以支持多種類型的數據。 這樣用戶就可以以自己的數據類型來使用組件。
創建第一個使用泛型的例子:identity函數。 這個函數會返回任何傳入它的值。 你可以把這個函數當成是 echo命令。
不使用泛型:
function identity(arg: number): number {
return arg;
}
//或者使用any來定義函數
function identity(arg: any): any {
return arg;
}
雖然使用any類型后這個函數已經能接收任何類型的arg參數,但是卻丟失了一些信息:傳入的類型與返回的類型應該是相同的。 如果我們傳入一個數字,我們只知道任何類型的值都有可能被返回。
因此,我們需要一種方法使返回值的類型與傳入參數的類型是相同的。 這里,我們使用了 類型變量,它是一種特殊的變量,只用于表示類型而不是值。
function identity<T>(arg: T): T {
return arg;
}
我們給identity添加了類型變量T。 T幫助我們捕獲用戶傳入的類型(比如:number),之后我們就可以使用這個類型。 之后我們再次使用了 T當做返回值類型。現在我們可以知道參數類型與返回值類型是相同的了。 這允許我們跟蹤函數里使用的類型的信息。
我們把這個版本的identity函數叫做泛型,因為它可以適用于多個類型。 不同于使用 any,它不會丟
我們定義了泛型函數后,可以用兩種方法使用。失信息,像第一個例子那像保持準確性,傳入數值類型并返回數值類型。
let output = identity<string>("myString"); // type of output will be 'string'
這里我們明確的指定了T是string類型,并做為一個參數傳給函數,使用了<>括起來而不是()。
第二種方法更普遍。利用了類型推論 -- 即編譯器會根據傳入的參數自動地幫助我們確定T的類型:
let output = identity("myString"); // type of output will be 'string'
注意我們沒必要使用尖括號(<>)來明確地傳入類型;編譯器可以查看myString的值,然后把T設置為它的類型。 類型推論幫助我們保持代碼精簡和高可讀性。如果編譯器不能夠自動地推斷出類型的話,只能像上面那樣明確的傳入T的類型,在一些復雜的情況下,這是可能出現的。
泛型類型
泛型函數的類型與非泛型函數的類型沒什么不同,只是有一個類型參數在最前面,像函數聲明一樣:
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
泛型類
泛型類看上去與泛型接口差不多。 泛型類使用( <>)括起泛型類型,跟在類名后面。
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };