泛型的概念
指不預先確定的數據類型,具體的類型要在使用的時候才能確定。咋一聽,是不是覺得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屬性
使用泛型有什么好處?
- 函數和類可以支持多種類型,增強程序的擴展性。
- 不必寫多條函數重載、冗長的聯合類型聲明,增強代碼可讀性。
- 靈活控制類型之間的約束
