前言
泛型是為了構(gòu)建通用化的組件所設(shè)計的,在強(qiáng)類型語言中,泛型是構(gòu)建大型復(fù)雜系統(tǒng)的主要特性之一,它允許將類型定義通用化,而不是單一的形式。
一、基本使用
function doSomeThing<T>(arg: T): T {
return arg;
}
相比于我們之前學(xué)習(xí)的類型定義的方式,這里我們多了一個<T>,在TypeScript中,這就叫泛型,表示的語義為,當(dāng)你調(diào)用這個函數(shù)的時候除了傳遞常用的參數(shù),你也可以傳遞類型,內(nèi)部會使用傳遞的這個類型。
比如,我們傳遞一個string類型:
doSomeThing<string>("mss")
值得注意的是,當(dāng)你使用了泛型,TypeScript會默認(rèn)會通過外部傳入的類型,推斷內(nèi)部變量或者函數(shù)的返回值的類型,并且強(qiáng)制內(nèi)部的類型要符合外部傳入的類型,否則,TypeScript會報錯:
function doSomeThing<T>(arg: T): T {
console.log(arg.length)
return arg;
}
// Property 'length' does not exist on type 'T'
但是我們有的數(shù)據(jù)類型就是存在length屬性的,那我們這時應(yīng)該怎么做呢?比如數(shù)組來說,TypeScript允許我們直接定義數(shù)組的泛型:
function deArr<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
此時我們訪問length就是合法的。
同時,定義數(shù)組的泛型也可以這樣寫,這種方式和上面那種方式是等效的:
function deAnotherArr<T>(arg: Array<T>): Array<T> {
console.log(arg.length);
return arg
}
這種方式也是可以,完全取決于你個人的喜好。
二、使用泛型類型
基本使用
function doSomeThing<T>(arg: T): T {
console.log(arg.length)
return arg;
}
let myDoSomeThing: <T>(arg: T) => T = doSomeThing;
傳入的類型參數(shù),也可以不用和函數(shù)定義的T保持一致:
let myDoSomeThing: <U>(arg: U) => U = doSomeThing;
這樣也是可以的。
三、泛型接口
基本使用:
我們首先定義一個泛型接口:
interface GenericInterFace {
<T>(arg: T): T
}
然后我們這樣使用:
let genericInterFace: GenericInterFace = function get<T>(arg: T):T {
return arg;
}
也可以在定義接口的時候?qū)魅?code>泛型的類型:
interface GenericInterFace<T> {
(arg: T): T
}
使用的方式和上面的例子一樣:
let genericInterFace: GenericInterFace = function get<T>(arg: T):T {
return arg;
}
此外,前面的例子里面,當(dāng)我們使用泛型的時候,由于TypeScript編譯器不能推斷傳入的T的類型,當(dāng)然這也不可能推斷,所以當(dāng)我們試圖在內(nèi)部訪問length屬性的時候,就會報錯。然后,我們這里可以利用接口,在編譯之前就給開發(fā)一個友好的提示:
定義一個length的接口:
interface Lengthwise {
length: number;
}
然后重寫我們前面的doSomeThing例子:
function doSomeThing<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg;
}
此時就不會報錯,這里已經(jīng)確保了傳入的參數(shù)必定是有length屬性的。
四、泛型類
泛型類在結(jié)構(gòu)上和泛型接口差不多。
class GenericClass<T> {
name: T
add: (x: T, y:T) => T
}
然后我們可以正常的通過new關(guān)鍵字進(jìn)行實例化。
let genericClass = new GenericClass();
genericClass.name = 'mss';
當(dāng)然,使用泛型的一個好處就是你可以傳入任意類型的類型參數(shù):
let genericClass = new GenericClass<string>();
這也是合理的。
五、泛型的高級用法
1、使用泛型確保訪問對象時不會訪問到對象不存在的屬性。
定義一個泛型:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
注意這里是的keyof是TypeScript中給我們提供的遍歷對象屬性值的操作符。
然后我們這樣使用:
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a");
// 1
getProperty(x, "m");
// Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.
2、類的構(gòu)造函數(shù)和泛型一起使用
function create<T>(c: { new (): T }): T {
return new c();
}