泛型是指定一個表示類型的變量弦蹂,用它來代替某個實際的類型用于編程,而后通過實際調(diào)用時傳入或推導的類型來對其進行替換较店,以達到一段使用泛型程序可以實際適應不同類型的目的雷客。為了實現(xiàn)泛型所要達到的目的,我們也可采用指定類型為 any 或者為每一種可能的類型都寫一個方法(重載)牺堰,但這嚴重違反抽象和復用代碼的原則佩微。所以在考慮可重用組件的時候,我們應該使用泛型萌焰。
泛型其實是 C# 和 Java 這種強類型語言中的一個特性哺眯,TypeScript 把這一特性引進到了弱類型語言 JavaScript 中,對于沒接觸過強類型語言的前端 coder扒俯,理解這個概念可能有點吃力奶卓,如果看不太懂一疯,可以先去了解一下 Java 中的泛型,下面進入正題夺姑。
初識泛型
定義泛型:我們把要傳入函數(shù)的參數(shù)類型設為一個類型變量 T 墩邀,它能夠幫助我們捕獲用戶傳入的類型,之后出現(xiàn) T 的地方盏浙,都會被替換成用戶傳入的類型眉睹。
function identity<T>(arg: T): T {
return arg;
}
function getFirst<T>(arr: T[]): T {
return arr[0];
}
console.log(identity(10)); // 10
console.log(identity('TS')); // TS
console.log(getFirst([1, 2, 3, 4])); // 1
console.log(getFirst(['a', 'b', 'c'])); // a
使用泛型:有兩種使用泛型的方式,第一種是傳入所有的參數(shù)废膘,包括類型參數(shù)竹海;第二種是不傳類型參數(shù),因為 TypeScript 的編譯器會利用類型推論來確定參數(shù)的類型丐黄,推薦使用第二種斋配。
function identity<T>(arg: T): T {
return arg;
}
function getFirst<T>(arr: T[]): T {
return arr[0];
}
// 使用一:傳入所有參數(shù)
console.log(identity<number>(10)); // 10
console.log(identity<string>('TS')); // TS
console.log(getFirst<number>([1, 2, 3, 4])); // 1
console.log(getFirst<string>(['a', 'b', 'c'])); // a
// 使用二:不傳參數(shù)類型
console.log(identity(10)); // 10
console.log(identity('TS')); // TS
console.log(getFirst([1, 2, 3, 4])); // 1
console.log(getFirst(['a', 'b', 'c'])); // a
泛型類型
一個泛型函數(shù)的類型如下:
<泛型變量名稱>(參數(shù)1: 泛型變量, 參數(shù)2: 泛型變量, ...參數(shù)n: 泛型變量) => 泛型變量
可以以對象字面量的形式來定義泛型函數(shù)(這更像是接口),如:
let foo: { <T>(arg: T): void };
foo = function <T>(arg: T): void {
console.log(arg);
}
foo(13); // 13
將上面的例子中的 { <T>(arg: T): void }
改為接口灌闺,則有:
interface IGeneric {
<T>(arg: T): void
}
let foo: IGeneric = function <T>(arg: T): void {
console.log(arg)
}
foo(13); // 13
最后艰争,接口中也可以使用泛型,這樣子就鎖定了代碼里可以使用的類型桂对,如:
interface IGeneric<T> {
(arg: T): void
}
function fn<T>(arg: T): void {
console.log(arg);
}
let myFn: IGeneric<number> = fn;
myFn(13); //13
泛型類
除了泛型接口甩卓,我們還可以創(chuàng)建泛型類(但是無法創(chuàng)建泛型枚舉、泛型命名空間)蕉斜,泛型類使用 <> 包圍泛型類型變量猛频,如:
class Person<T> {
love: T;
say: (arg: T) => T;
}
let me = new Person<string>();
me.love = 'TS';
// me.love = 520; // ERROR
me.say = function(love: string){
return `my love is ${love}`;
}
console.log(me.say('TS')); // my love is TS
注意:類有兩部分,分為靜態(tài)部分和實例部分蛛勉,泛型類的類型指的是實例部分的類型鹿寻,靜態(tài)屬性不能使用該泛型類型。
泛型約束
泛型可以通過 extends 一個接口來實現(xiàn)泛型約束诽凌,寫法如:<泛型變量 extends 接口>
毡熏,例子:
interface IArray {
length: number
}
function logIndex<T extends IArray>(arg: T): void {
for (let i = 0; i < arg.length; ++i) {
console.log(i)
}
}
let arr = [1, 2, 3]
// logIndex<number>(arr) // 報錯
logIndex<number[]>(arr) // 允許
logIndex(arr) // 自動類型推導,允許
可以在泛型里使用類類型侣诵,如使用泛型創(chuàng)建工廠函數(shù)痢法,需要引用構(gòu)造函數(shù)的類類型,有:
function create<T>(c: { new(): T }): T {
return new c()
}