技術(shù)分享!TypeScript學(xué)習(xí)筆記(八)- 泛型
泛型
泛型(Generics)是指在定義函數(shù)、接口或類(lèi)的時(shí)候,不預(yù)先指定具體的類(lèi)型,而在使用的時(shí)候再指定類(lèi)型的一種特性。
我們來(lái)實(shí)現(xiàn)一個(gè)函數(shù) createArray,它可以創(chuàng)建一個(gè)指定長(zhǎng)度的數(shù)組,同時(shí)將每一項(xiàng)都填充一個(gè)默認(rèn)值:
function createArray(length: number, value: any): Array<any> {
? ? let result = [];
? ? for (let i = 0; i < length; i++) {
? ? ? ? result[i] = value;
? ? }
? ? return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
復(fù)制代碼
我們使用了之前提到過(guò)的數(shù)組泛型來(lái)定義返回值的類(lèi)型。
這段代碼編譯不會(huì)報(bào)錯(cuò),但是一個(gè)顯而易見(jiàn)的缺陷是,它并沒(méi)有準(zhǔn)確的定義返回值的類(lèi)型:
Array 允許數(shù)組的每一項(xiàng)都為任意類(lèi)型。但是我們預(yù)期的是,數(shù)組中每一項(xiàng)都應(yīng)該是輸入的 value 的類(lèi)型。
function createArray<T>(length: number, value: T): Array<T> {
? ? let result: T[] = [];
? ? for (let i = 0; i < length; i++) {
? ? ? ? result[i] = value;
? ? }
? ? return result;
}
createArray<string>(3, 'x'); // ['x', 'x', 'x']
復(fù)制代碼
我們?cè)诤瘮?shù)名后添加了 ,其中 T 用來(lái)指代任意輸入的類(lèi)型,在后面的輸入 value: T 和輸出 Array 中即可使用了。
接著在調(diào)用的時(shí)候,可以指定它具體的類(lèi)型為 string。當(dāng)然,也可以不手動(dòng)指定,而讓類(lèi)型推論自動(dòng)推算出來(lái):
function createArray<T>(length: number, value: T): Array<T> {
? ? let result: T[] = [];
? ? for (let i = 0; i < length; i++) {
? ? ? ? result[i] = value;
? ? }
? ? return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
復(fù)制代碼
多個(gè)類(lèi)型參數(shù)
定義泛型的時(shí)候,可以一次定義多個(gè)類(lèi)型參數(shù):
function swap<T, U>(tuple: [T, U]): [U, T] {
? ? return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
復(fù)制代碼
上例中,我們定義了一個(gè) swap 函數(shù),用來(lái)交換輸入的元組。
泛型約束
在函數(shù)內(nèi)部使用泛型變量的時(shí)候,由于事先不知道它是哪種類(lèi)型,所以不能隨意的操作它的屬性或方法:
function loggingIdentity<T>(arg: T): T {
? ? console.log(arg.length);
? ? return arg;
}
// index.ts(2,19): error TS2339: Property 'length' does not exist on type 'T'.
復(fù)制代碼
上例中,泛型 T 不一定包含屬性 length,所以編譯的時(shí)候報(bào)錯(cuò)了。
這時(shí),我們可以對(duì)泛型進(jìn)行約束,只允許這個(gè)函數(shù)傳入那些包含 length 屬性的變量。這就是泛型約束:
interface Lengthwise {
? ? length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
? ? console.log(arg.length);
? ? return arg;
}
復(fù)制代碼
上例中,我們使用了 extends 約束了泛型 T 必須符合接口 Lengthwise 的形狀,也就是必須包含 length 屬性。
此時(shí)如果調(diào)用 loggingIdentity 的時(shí)候,傳入的 arg 不包含 length,那么在編譯階段就會(huì)報(bào)錯(cuò)了:
interface Lengthwise {
? ? length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
? ? console.log(arg.length);
? ? return arg;
}
loggingIdentity(7);
// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.
復(fù)制代碼
多個(gè)類(lèi)型參數(shù)之間也可以互相約束:
function copyFields<T extends U, U>(target: T, source: U): T {
? ? for (let id in source) {
? ? ? ? target[id] = (<T>source)[id];
? ? }
? ? return target;
}
let x = { a: 1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 });
復(fù)制代碼
上例中,我們使用了兩個(gè)類(lèi)型參數(shù),其中要求 T 繼承 U,這樣就保證了 U 上不會(huì)出現(xiàn) T 中不存在的字段。
泛型接口
之前學(xué)習(xí)過(guò),可以使用接口的方式來(lái)定義一個(gè)函數(shù)需要符合的形狀:
interface SearchFunc {
? (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
? ? return source.search(subString) !== -1;
}
復(fù)制代碼
當(dāng)然也可以使用含有泛型的接口來(lái)定義函數(shù)的形狀:
interface CreateArrayFunc {
? ? <T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc;
createArray = function<T>(length: number, value: T): Array<T> {
? ? let result: T[] = [];
? ? for (let i = 0; i < length; i++) {
? ? ? ? result[i] = value;
? ? }
? ? return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
復(fù)制代碼
進(jìn)一步,我們可以把泛型參數(shù)提前到接口名上:
interface CreateArrayFunc<T> {
? ? (length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc<any>;
createArray = function<T>(length: number, value: T): Array<T> {
? ? let result: T[] = [];
? ? for (let i = 0; i < length; i++) {
? ? ? ? result[i] = value;
? ? }
? ? return result;
}
createArray(3, 'x'); // ['x', 'x', 'x']
復(fù)制代碼
注意,此時(shí)在使用泛型接口的時(shí)候,需要定義泛型的類(lèi)型。
泛型類(lèi)
與泛型接口類(lèi)似,泛型也可以用于類(lèi)的類(lèi)型定義中:
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; };
復(fù)制代碼
泛型參數(shù)的默認(rèn)類(lèi)型
在 TypeScript 2.3 以后,我們可以為泛型中的類(lèi)型參數(shù)指定默認(rèn)類(lèi)型。當(dāng)使用泛型時(shí)沒(méi)有在代碼中直接指定類(lèi)型參數(shù),從實(shí)際值參數(shù)中也無(wú)法推測(cè)出時(shí),這個(gè)默認(rèn)類(lèi)型就會(huì)起作用。
function createArray<T = string>(length: number, value: T): Array<T> {
? ? let result: T[] = [];
? ? for (let i = 0; i < length; i++) {
? ? ? ? result[i] = value;
? ? }
? ? return result;
}
作者:轉(zhuǎn)眼歲歲又年年
鏈接:https://juejin.cn/post/6913530341202329607
來(lái)源:掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。