TypeScript中泛型函数
一.概览
此前,对泛型有了整体的概览,详见TypeScript中的泛型,后面的系列会详细地介绍TypeScript的泛型。此篇文章主要介绍泛型函数
二. 泛型函数
泛型是类型不明确的数据类型,在定义时,接收泛指的数据类型(不知道具体是什么类型),在使用时,指定明确的数据类型的一种给定类型的方式。
泛型定义的位置:
- 函数声明: 写在函数名后面;
- 函数表达式: 写在表达式最前面
泛型调用的位置
在函数名后面跟泛型
传入具体的数据类型![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/70597a0664884784b3270b4e7e35e70a.png
1.泛型函数的引入具体是为了解决什么问题呢?
举个例子
function getSplitValue(value: string[],type: string):string {
return value.join(type)
}
function getSplitValue2(value: number[],type: string):string {
return value.join(type)
}
getSplitValue(['1','2','3'],',')
getSplitValue2([1,2,3],',')
问题:两个函数内部的逻辑完全一样,但由于传递参数的类型不同,导致了不得不定义两个函数去处理两个不同类型的数组
解决方案:用一种类型兼容两种不同类型的数组
第一种方法,可以使用any
value: any[]
缺点:如果想在使用函数时明确数组元素类型时,any就不适合了,用any会使约束变小
第二种方法: 使用泛型
function getSplitValue3<E>(value: E[],type: string):string {
return value.join(type)
}
const aa = getSplitValue3<string>(['a','b','c'],',')
const bb = getSplitValue3<number>([1,2,3],',')
console.log(aa) // 'a,b,c'
console.log(bb) // '1,2,3'
最小范围为any的场景,基本上都可以使用泛型
2. 泛型函数复用
type TypePlus<T> = (a: T, b: T) => T
type TypeNumberPlus<T,U> = (a: T, b: U) => string
const plusNumber: TypePlus<number> = (a,b) => {
return a + b
}
const plusString: TypePlus<string> = (a,b) => {
return a + b
}
const numberPlus: TypeNumberPlus<number,string> = (a,b) => {
return a + b
}
console.log(plusNumber(1,2))
console.log(plusString('1','2'))
console.log(numberPlus(9,'aa'))
可以将公共部分封装起来,有点像js中的柯里化。
3. 泛型约束
举个例子
function plusNumber(a: number,b:number):number {
return a + b
}
指定特定类型,正常编译
function plusNumber<T>(a: T,b:T):number {
return a + b
}
指定泛型,编译报错
编译报错:运算符“+”不能应用于类型“T”和“T”
报错原因:它们可以是任何类型(由泛型 T 表示),然后返回这两个参数的和。
但是,有一个问题在于,这个函数不能正确处理所有类型的参数。当输入参数的类型不是数字或时,尝试将它们相加将会导致运行时错误,所以需要约束
function plusNumber<T extends number>(a: T, b: T): number {
return a + b
}
plusNumber<number>(1,2)
类似的例子还有这种
function getLength<T>(value: T) {
return value.length
}
编译报错:类型“T”上不存在属性“length”
报错原因:泛型T作为getLength的泛型参数的类型范围大了,某些数据类型是没有length属性,导致无法进行length的访问
可以用泛型约束来修复
function getLength<T extends {length: number}>(value: T) {
return value.length
}
注意:函数约束是为了让范围的可代表类型的范围缩小,并不是给泛型指定类型。
可迭代的对象,才可以进行对应的数据转换
interface IPrev<U> {
[key:number]:U
}
function createObject<T extends Iterable<any>>(value: T) {
return [...value].reduce((prev:IPrev<U>,current:U,index:number) =>{
prev[index] = current
return prev
},{})}
4. 泛型的联合类型
举个例子
function mergeArr<E> (arr1: E[],arr2:E[]) {
return [...arr1,...arr2]
}
const arr = mergeArr<number>([1,2,3],['a','b','c'])
类型推断: 根据第一个实参[1,2,3]推断E为number,所以[‘a’,‘b’,‘c’]里面的值为字符串的时候会有编译报错
编译报错:不能将类型“string”分配给类型“number”。
这个时候就可以使用联合类型
type typeValue = string | number
function mergeArr<E> (arr1: E[],arr2:E[]) {
return [...arr1,...arr2]
}
const arr = mergeArr<typeValue>([1,2,3],['a','b','c'])
三.总结
泛型的好处:
- 可以在不明确类型的时候,使用泛型占位
- 可以在调用函数是,对函数的参数进行类型的约束
类型参数化是泛型的特征:
- 定义时,传入类型参数(泛型)
- 调用时,传入实际类型