泛型的概念
指不預先確定的數據類型,具體的類型要在使用的時候才能確定。咋一聽,是不是覺得JavaScript本身就是這樣?這是由於理解有誤。前面說“在使用的時候確定”,而非在程序執行的時候確定。
泛型函數
現在有個需求:一個被定義的函數原本輸入字符串輸出字符串,現在想讓它同時支持輸入輸出字符串數組,如何實現?
1.通過函數重載
// 函數調用時依照聲明的順序進行匹配 function log(value: string): string; function log(value: string[]): string[]; function log(value: any): any{ console.log(value); return value; } log("abc"); log(["10","abc"]);
2.使用聯合參數
function logs(value: string | string[]): string | string[]{ return value }
以上兩種都OK,但是不夠簡潔,不夠靈活。下面使用泛型。
function log1<T>(value: T): T{ return value } 等價於 let log1 = <T>(value: T) => { return value }; log1<string>("hello"); // 調用的時候指定類型 log1<string[]>(["hi","ha"]);
泛型接口
// 要注意<T>的位置,前者在使用時必須指定類型,后者在使用時無須指定類型 interface Log<T> { (value: T): T; } let log3: Log<number> = (v) => { console.log("必須指定類型",v);return v }; log3(12); interface Log{ <T>(value: T): T; } let log3: Log = (v) => { console.log("無須指定類型",v);return v}; log3<number>(10); // 無須指定類型,如果要指定類型,在調用的時候指定 log3(5);
泛型類
對類的成員進行約束,注意不能約束靜態成員。
class Log<T> { run(value: T) { console.log(value); return value } } let log1 = new Log<number>(); // 可以進行約束 log1.run(1); let log2 = new Log(); // 也可以不進行約束 log2.run("2");
泛型約束
泛型約束是指,傳入的參數必須符合約束,不符合約束的參數無法傳入。
function log<T>(value: T):T{ console.log(value.length); // 如果訪問.length屬性,TS編譯器會報錯,因為不知道value有沒有這個屬性 return value } 此時使用泛型約束 interface Length { length: number; type?: string; } // extends Length表示允許value參數通過.操作符訪問Length中定義的屬性 function log<T extends Length>(value: T): T{ console.log(value, value.length,value.type); return value } // 所有具有length屬性的值,都可以被當做參數傳入log函數 log([1,2,3]); log("123"); log({length: 1});
在這個例子中,約束傳入的參數必須具有length屬性
使用泛型有什么好處?
- 函數和類可以支持多種類型,增強程序的擴展性。
- 不必寫多條函數重載、冗長的聯合類型聲明,增強代碼可讀性。
- 靈活控制類型之間的約束