为什么要使用泛型
function fn(arr, n: number) {
return arr
}
在上面函数中第一个参数是个数组,第二个参数我们可以确定是个number类型的数字,但是第一个参数我们无法断定
它是number[]的数组,还是string[]的数组,它可以是任何类型的数组,既然是任何类型那我们是不是可以写成any[]类型
function fn(arr: any[], n: number):any[] {
return arr
}
函数的参数,返回值都是any[]类型的数组,当我们给函数传入一个string[]类型的数组,
const newArr = fn(['1', '2', '3', '4', '5', '6'], 3)
按理来说,在函数里面因该能识别出函数的参数类型,函数的返回值类型都是string[]类型,结果显而易见无法识别,
所以这里使用any类型是不行的,所以这个时候我们就得使用泛型来解决这个问题。总的来说,有时,书写某个函数时,会丢失一些类型信息(多个位置的类型应该保持一致或有关联的信息)
什么是泛型
泛型是指附属于函数、类、接口、类型别名之上的类型,当某个函数的参数,返回值和内部使用时的类型无法确定的情况下,就可以使用泛型来进行约束
如何使用或书写泛型
在函数名之后写上fn<泛型名>
,通常泛型名使用大写的T,当然你可以随意命名,在调用的时候函数名字后面加上fn<泛型类型>
function fn<T>(arr:any[], n: number):any[] {}
fn<number>(['1','2','3'],2)
泛型类型“T”就像一个参数,可供传递,如我们在调用函数时在尖括号中传入,则这个T(泛型)的类型就是number,
这样我们就可以any类型都换成泛型就可以了,这样就当泛型参数传入number类型时,函数的返回值就是number类型的数组
function fn<T>(arr:T[], n: number) :T[]{
const newArr: T[] = [];
return newArr
}
const newArr = fn<number>([1, 2, 3, 4, 5, 6], 3)
这里 T类型就是number,TS就会识别类型,当然你也可以不传入泛型类型,如下
function fn<T>(arr:T[], n: number) :T[]{
if (n >= arr.length) {
return arr
}
const newArr: T[] = [];
for (let i = 0; i < n; i++) {
newArr.push(arr[i])
}
return newArr
}
const newArr = fn([1, 2, 3, 4, 5, 6], 3)
这时TS会自动识别[1, 2, 3, 4, 5, 6]函数第一个参数你传入的是一个number[]类型的数组,fn<T>(arr:T[], n: number) :T[]{
从而推导出arr:T[]
中的T泛型是number类型,这样其他几个T泛型也都是number,这样最终的返回结果newArr也是number[]类型的数组
说到这里不知道大家有没有疑问,当如果我不传递T泛型参数时,我的数组是长这样的[1, 2, '4', 5],那它返回的是什么类型?
是any吗?还是说还是一个数组,只不过是里面既有number又有string类型的数组,当然是第二种的只不过这个时候表示的是(string | number)[]的写法
function fn<T>(arr:T[], n: number) :T[]{
if (n >= arr.length) {
return arr
}
const newArr: T[] = [];
for (let i = 0; i < n; i++) {
newArr.push(arr[i])
}
return newArr
}
const newArr = fn([1, 2, '4', 5, 5, 6], 3)
当然这种是在你不传递Tf泛型参数的情况下才能这样的,如果又泛型参数,就只能规规矩矩的进行了
总结在函数中使用泛型
泛型相当于是一个类型变量,在定义时,无法预先知道具体的类型,可以使用该变量来代替,只有到调用时,才能确定它的类型
很多时候,TS会只能的根据传递参数,推导泛型的具体类型,如果无法推导,并且又没有传递具体的类型,则会返回空对象的类型
当然泛型也可以设置默认值,当没有传递时就使用默认值,就像js中函数中的参数一样设置默认值
在类型别名、接口、类型中使用泛型
直接在名称后面写上<泛型名称>
- 类型别名
例子: 回调函数:判断数组中的某一项是否满足条件
type callback= (n:number,i:number)=>boolean;
该类型只能传number类型的数组,这就有弊端,我不一定数组就是number类型的,不确定类型,
type callback<T>= (n:T,i:number)=>boolean;
这个时候就可以使用泛型了,只要传入类型泛型类型T,n的类型也是T,这样就可以根据传入的类型就可以了
- 接口:这个和类型别名类似 例子如下
interface callback<T>{(n:T,i:number):boolean;}
//类似js数组中的filter方法
function filter<T>(arr: T[], callback: callback<T>): T[] {
const newArr: T[] = [];
arr.forEach((n,i)=>{
if(callback(n,i)){
newArr.push(n);
}
})
return newArr
}
- 类
先看例子,这样写handle1和handle2的泛型没有任何联系,在调用的时候传什么就是什么
class arrayHelper{
handle1<T>(arr:T[]):T[]{}
handle2<T>(arr:T[]):T[]{}
}
这样写就没啥意思,我们想能只要传一个就代表所有,如下
class arrayHelper<T>{
constructor(private arr:T[]) {
}
handle1(n:number){
//使用arr的时候就用this.arr就行了
let newArr:T[]=this.arr
}
handle2(){
let newArr:T[]=this.arr
}
}
这样就不用在每次调用函数的时候去传入泛型,而是在创建类型时去传入,这样就可以整个类都是该类型