泛型
TS中可以使用泛型創建可重用的組件,一個組件可以支持多種類型的數據,這樣用戶就可以以自己的數據類型來使用組件。
泛型變量
通過泛型變量,可以知道用戶傳入的類型,從而可以以這個類型作爲函數的返回類型。
function identity<T>(arg: T): T {
return arg
}
當然也可以直接通過類型推斷
let res = identity('hello world') // 編譯時,類型推斷則會推斷這個數據類型爲 string
值得注意的是,對於泛型變量,它是可以代表任意類型的,如果不指定泛型變量的類型,就在函數中操作指定的類型變量所具有的方法,就會報錯。
例如:
function identity<T>(arg: T): T {
console.log(arr.length)
return arg // 此時會報錯,T並沒有length這個屬性
}
解決方法
function identity<T>(arg: T[]): T[] {
console.log(arr.length)// 此時就不會報錯,因爲T[]可以代表任意數據類型的數組
return arg
}
也可以這樣實現
function loggingIdentity<T>(arg: Array<T>): Array<T> {
console.log(arg.length)
return arg
}
此時這個泛型就代表任意數據類型的數組
泛型接口
- 常見的使用方式
interface GenericIdentityFn {
<T>(arg: T): T
}
function identity<T>(arg: T): T {
return arg
}
let myIdentity: GenericIdentityFn = identity
相當於
function identity<T>(arg: T): T {
return arg
}
let myIdentity: {<T>(arg: T): T} = identity
即把 {(arg: T): T} 定義成一個接口使用
- 將泛型參數當作整個接口的一個參數使用
interface GenericIdentityFn<T> {
(arg: T): T
}
function identity<T>(arg: T): T {
return arg
}
let myIdentity: GenericIdentityFn<number> = identity
泛型類
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 }
需要注意的是,泛型類限制的只是類的實例部分的類型,對於靜態部分不能使用泛型類。
泛型約束
泛型約束顧名思義在泛型的前提下加上一定的約束條件。
例如:
interface Lengthwise {
length: number
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg
}
loggingIdentity(3) // 報錯 3是number類型,並具有length這個屬性
loggingIdentity({length: 3, value: 3 }) // 不會報錯,因爲傳入的變量具有length這個屬性
- 在泛型約束中使用類型參數
function getProperty(obj: T, key: K) {
return obj[key]
}
let x = { a: 1, b: 2, c: 3, d: 4 }
getProperty(x, "a") // 成功
getProperty(x, "m") // 報錯 因爲x這個對象上沒有m這個屬性
這段代碼就是通過類型約束,將Key規定爲必須在obj上,即可以理解爲這個對象上的屬性
- 在泛型裏使用類類型
class BeeKeeper {
hasMask: boolean
}
class ZooKeeper {
nametag: string
}
class Animal {
numLegs: number
}
class Bee extends Animal {
keeper: BeeKeeper
}
class Lio extends Animal {
keeper: Zookeeper
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c()
}
createInstance(Lio).keeper.nametag
createInstance(Bee).keeper.hasMask