开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第5天,点击查看活动详情
什么是泛型?
在网上对于泛型的解释有很多,在我看来,泛型指的就是一种“类型变量”,即在定义函数,接口,类的时候,先用一个变量指定类型,而该“类型变量”所代表的真正类型在使用时候才真正确定。
泛型函数(在函数中定义泛型)
假如我们在ts中想写一个函数,接收一个数字类型的参数,并将其返回,可通过如下代码
function echoFn(ar: number):number { return ar }
但假如我们希望可以传入任意的参数类型,并将其返回呢?此时可以在方法上定义一个“类型变量”(用尖括号包裹),如
function echoFn<Type>(ar: Type):Type { return ar }
此时Type即我们在函数上定义的泛型,它真正的类型在调用时才确定,如
echoFn<string>('i am string') //此时Type为string
echoFn<number>(1) //此时Type为number
//其中<string> <number>等为显示指定Type类型,但ts通过传参推断Type的类型,所以上面调用可以简写为
echoFn('i am string') //此时Type为string
echoFn(1) //此时Type为number
泛型函数类型
泛型函数也是一种类型,以function echoFn<Type>(ar: Type):Type { return ar }
为例,表型泛型函数类型有以下几种:
1. let eF: <Type>(ar:Type) => Type = echoFn
2. 第一点中的Type其实可以为任意名称,如 let eF: <AnyType>(ar:AnyType) => AnyType = echoFn
3. 写成对象形式 let ef: { <Type>(ar:Type): Type } = echoFn
泛型接口
泛型接口的书写形式和“泛型函数类型”的对象形式有点像,如
function echoFn<Type>(ar: Type):Type { return ar }
interface EnchoFnInterface {
<Type>(ar: Type): Type;
}
let ef:EnchoFnInterface = echoFn
此处EnchoFnInterface表示某对象具备一个函数,接受一个泛型类型,并返回。泛型接口中泛型的定义也可以拓展到整个接口范围,如
function echoFn<Type>(ar: Type):Type { return ar }
interface EnchoFnInterface<Type>{
(ar: Type): Type;
}
let ef:EnchoFnInterface<string> = echoFn
通过两个例子对比,眼尖的朋友可能观察到,在整个接口范围定义的泛型,在使用该泛型接口时,就需要将“泛型变量”的具体内容传入,即let ef:EnchoFnInterface<string> = echoFn
处的<string>
泛型类
泛型类的用法是泛型接口很像,如
class GenericNumber<NumType> {
add: undefined |((x: NumType, y: NumType) => NumType) = undefined;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.add = function (x, y) {
return x + y;
};
console.log(myGenericNumber.add(1,2))
console.log(myGenericNumber.add(1,'2')) // Argument of type 'string' is not assignable to parameter of type 'number'.
type方式定义泛型
类似interface可定义泛型,type可以建立泛型,如
type someTypes<Type> = Type | Type[]
let st:someTypes<string> = 'str'
上面定义了SomeTypes类型,其可以是Type也可以是Type类型的数组。 官方示例
对泛型进行限制
看如下例子
function echoFn<Type>(ar: Type):Type {
console.log(ar.length) //报错:Property 'length' does not exist on type 'Type'
return ar;
}
此处报错是因为Type类型变量在传参时候才能确定具体类型,因此不能保证ar具备length属性,所以报错,此时我们需要限制ar传入的是具备length属性的对象,如
interface LengthConstraint {
length: number
}
function echoFn<Type extends LengthConstraint>(ar: Type):Type {
console.log(ar.length) // 此处不再报错
return ar;
}
//当我们想传入一些没有具备length属性的对象时,会报错
echoFn(1) // Argument of type 'number' is not assignable to parameter of type 'LengthConstraint'
创建限制于另一个泛型的泛型,如
function getObjVal<Type,Key extends keyof Type>(target:Type,key:Key){
return target[key]
}
const obj = { name: 'JoeZhou'};
getObjVal(obj,'name');
getObjVal(obj,'age');// Argument of type '"age"' is not assignable to parameter of type '"name"'//
创建受限于某类的实例
class Animal {
sound = 'animal'
}
class People{
name = 'people'
}
function getAInstance<Type extends Animal>(I: new ()=> Type){
return new I()
} // 此处限定要接受Animal类型的构造函数,即拥有sound属性的类
getAInstance(Animal)
getAInstance(People)//Argument of type 'typeof People' is not assignable to parameter of type 'new () => Animal'.
//假如给People也加上sound属性,但不继承Animal也不会报错