约束泛型
假设现在有这么一个函数,打印传入参数的长度,我们这么写:
function printLength<T>(arg: T): T {
console.log(arg.length)
return arg
}
因为不确定 T 是否有 length 属性,会报错:
可以和 interface 结合,来约束类型。
interface ILength {
length: number
}
function printLength<T extends ILength>(arg: T): T {
console.log(arg.length)
return arg
}
这其中的关键就是 <T extends ILength>
,让这个泛型继承接口 ILength
,这样就能约束泛型。
我们定义的变量一定要有 length 属性,比如下面的 str、arr 和 obj,才可以通过 TS 编译。
const str = printLength('lin')
const arr = printLength([1,2,3])
const obj = printLength({ length: 10 })
只要你有 length 属性,都符合约束,那就不管你是 str,arr 还是obj,都没问题。
当然,我们定义一个不包含 length 属性的变量,比如数字,就会报错:
泛型的一些应用
使用泛型,可以在定义函数、接口或类的时候,不预先指定具体类型,而是在使用的时候再指定类型。
泛型约束类
定义一个栈,有入栈和出栈两个方法,如果想入栈和出栈的元素类型统一,就可以这么写:
class Stack<T> {
private data: T[] = []
push(item:T) {
return this.data.push(item)
}
pop():T | undefined {
return this.data.pop()
}
}
在定义实例的时候写类型,比如,入栈和出栈都要是 number 类型,就这么写:
const s1 = new Stack<number>()
这样,入栈一个字符串就会报错:
这是非常灵活的,如果需求变了,入栈和出栈都要是 string 类型,在定义实例的时候改一下就好了:
const s1 = new Stack<string>()
特别注意的是,泛型无法约束类的静态成员。
给 pop 方法定义 static
关键字,就报错了
静态成员不能引用类类型参数。(2302)
泛型约束接口
使用泛型,也可以对 interface 进行改造,让 interface 更灵活。
interface IKeyValue<T, U> {
key: T
value: U
}
const k1:IKeyValue<number, string> = {
key: 18,
value: 'lin'
}
const k2:IKeyValue<string, number> = {
key: 'lin',
value: 18
}
泛型定义数组
定义一个数组,我们之前是这么写的:
const arr: number[] = [1,2,3]
// 泛型
const arr: Array<number> = [1,2,3]
小结
泛型
(Generics),从字面上理解,泛型就是一般的,广泛的。
泛型是指在定义函数、接口或类的时候,不预先指定具体类型,而是在使用的时候再指定类型。
泛型中的 T
就像一个占位符、或者说一个变量,在使用的时候可以把定义的类型像参数一样传入,它可以原封不动地输出。
泛型在成员之间提供有意义的约束,这些成员可以是:函数参数、函数返回值、类的实例成员、类的方法等。