泛型
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