泛型
泛型(Generics)是指在定義函數、接口或者類的時候, 不預先指定其類型,而是在使用是手動指定其類型的一種特性。
舉個栗子
我們需要創建一個函數, 這個函數會返回任何它傳入的值。
正常代碼如下:
function identity(arg: any): any {
return arg
}
identity(3) // 3
這代代碼編譯不會出錯,但是存在一個顯而易見的缺陷, 就是沒有辦法約束輸出的類型與輸入的類型保持一致。
這時,我們可以使用泛型來解決這個問題;
function identity<T>(arg: T): T {
return arg
}
identity(3) // 3
上例中,我們在函數名后面加了 <T>
, 其中的 T
表示任意輸入的類型, 后面的 T
即表示輸出的類型,且與輸入保持一致。
當然我們也可以在調用時手動指定輸入與輸出的類型, 如上述函數指定 string
類型:
identity<number>(3) // 3
泛型約束
在泛型函數內部使用類型變量時, 由於事先並不知道它是那種類型, 所以不能隨意操作它的屬性和方法:
function loggingIdentity<T>(arg: T): T {
console.log(arg.length) // err
return arg
}
上述函數中 類型 T 上不一定存在 length 屬性, 所以編譯的時候就報錯了。
這時,我們可以的對泛型進行約束,對這個函數傳入的值約束必須包含 length
的屬性, 這就是泛型約束:
interface lengthwise {
length: number
}
function loggingIdentity<T extends lengthwise>(arg: T): T {
console.log(arg.length) // err
return arg
}
loggingIdentity({a: 1, length: 1}) // 1
loggingIdentity('str') // 3
loggingIdentity(6) // err 傳入是參數中未能包含 length 屬性
這樣我們就可以通過泛型約束的方法對函數傳入的參數進行約束限制。
多個參數時也可以在泛型約束中使用類型參數
如你聲明了一個類型參數, 它被另一類型參數所約束。現在想要用屬性名從對象里湖區這個屬性。並且還需確保這個屬性存在於這個對象上, 因此需要咋這兩個類型之間使用約束,
簡單舉例來說: 定義一個函數, 接受兩個參數 第一個是個對象 obj,第二個個參數是第一參數 key 是對象里面的鍵名, 需要輸入 obj[key]
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
let obj = { a: 1, b: 2, c: 3 }
getProperty(obj, 'a') // success
getProperty(obj, 'm') // err obj 中不存在 m 這個參數
泛型接口
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>
}
let createArr: CreateArrayFunc
createArr = 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']
進一步的, 還可以把泛型參數提前到接口名上:
interface CreateArrayFunc<T> {
(length: number, value: T): Array<T>
}
let createArr: CreateArrayFunc<any>
createArr = 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']
注意: 此時使用泛型接口時, 需要定義泛型的類型
泛型類
與泛型接口類型,泛型也可以定義在類的類型定義中:
class GenericNumber<T> {
zeroValue: T
add: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroNumber = 0
myGenericNumber.add = function(x, y) {
return x + y
}
注意: 類有兩部分: 靜態部分和實例部分。泛型類值的是實例部分的類型,所以類的靜態屬性不能使用這個泛型類型
泛型參數的默認類型
在 TypeScript 2.3 以后,我們可以為泛型中的類型參數指定默認類型。當使用泛型時沒有在代碼中直接指定類型參數,從實際值參數中也無法推測出時,這個默認類型就會起作用。
function createArr<T = string>(length: number, value: T): Array<T> {
let result: T[] = []
for( let i = 0; i < lenght; i++ ) {
result[i] = value
}
return result
}