TypeScript泛型

在编程中,定义一个变量不确定类型的时候有两种解决方式:
1.使用any
2.使用泛型

什么是泛型

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候在指定类型的一直特征。

为什么要使用泛型

因为any会在调试中丢失很多的信息,所以我们不建议使用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)

当然这种是在你不传递T泛型参数的情况下才能这样,如果又泛型参数,就只能规规矩矩的进行了。
使用方式类似于函数传参,传什么数据类型,T就表示什么数据类型, 使用时,T也可以换成任意字符串。
泛型相当于是一个类型变量,在定义时,无法预先知道具体的类型,可以使用该变量来代替,只有到调用时,才能确定它的类型

在接口中使用泛型

//形式一
interface Search {
  <T,Y>(name:T,age:Y):T
}
let fn:Search = function <T, Y>(name: T, id:Y):T {
  console.log(name, id)
  return name;
}
fn('li',11);//编译器会自动识别传入的参数,将传入的参数的类型认为是泛型指定的类型

//形式二
interface Search<T, Y> {
  name: T,
  age: Y
}
function fn<T, U> (name: T, age: U): Search<T, U> {
  console.log(name + ": " + typeof (name));//章三:string
  console.log(age + ": " + typeof (age));//28:number
  let user: Search<T, U> = {
    name,
    age
  };
  return user;
}
console.log(fn('章三', 28));//{name: '章三', age: 28}

在类中使用泛型

class Animal<T> {
 name:T;
 constructor(name: T){
  this.name = name;
 }
 say<T>(say:T) {
   console.log(say)
 }
}
let cat = new Animal('cat');
cat.say('mimi')

泛型约束

使用接口约束泛型

interface Animal<U> {
  name: U
  run: () => U
}
//接口泛型约束类属性
class AnimalClass<T> implements Animal<T> {
  name: T
  constructor(name: T) {
    this.name = name
  }
  run(): T {
    return this.name + '在跑步'
  }
}
const animal1 = new AnimalClass<String>('小黄狗');
console.log(animal1.run()); // 小黄狗在跑步
const animal2 = new AnimalClass<Number>(666);
console.log(animal2.run()); // 666在跑步

  转载请注明: 小浩之随笔 TypeScript泛型

 上一篇
TypeScript装饰器 TypeScript装饰器
什么是装饰器 装饰器是给一个方法,属性或类扩展一些新的行为,而不是去直接修改它本身的一个函数。 如果在项目中要使用装饰器,需要在 tsconfig.json 的编译配置中开启experimentalDecorators,将它设为 true。
2021-11-15
下一篇 
TypeScript中Interface与Type TypeScript中Interface与Type
Interface Interface可以使用extends和implements。它在声明的那一刻并不是最终类型,由于interface可以进行声明合并,在创建后是可变的,可以将新成员添加到同一个interface中。 Type 可以声明
2021-10-24
  目录