Typescript 之泛型

519 阅读2分钟

小知识,大挑战!本文正在参与“  程序员必备小知识  ”创作活动

本文同时参与 「掘力星计划」  ,赢取创作大礼包,挑战创作激励金

为什么要用泛型

  1. 定义时就限制类型
    function echo(arg:number):number{
        return arg
    }
    
    只能传入number类型,无法传入string等类型
    const result = echo(2)
    
  2. 采用any
    function echo(arg:any):any{
        return any
    }
    
    是可以支持传入任何类型,可传入与返回的类型可以不一致。比如我们输入个number类型,我们只知道任何类型的值都有可能返回

泛型

定义

在定义函数,接口或类时,不预先指定具体类型,而是在使用的时候动态填入确定类型值

使用

在函数名后加上,其中T可以换成任意字符。T帮助捕获传入的类型(比如 number),之后我们就可以在参数以及返回值里用这个类型T,就达到了传入类型与返回类型一致的效果
示例1:

function echo<T>(arg:T):T{
    return any
}
const result = echo(123)

示例2

function swap<T,U>(tuple:[T,U]):[U,T]{
  return [tuple[1],tuple[0]]
}
const s = swap(["23",23])

约束泛型

为什么会有约束泛型

function echoWithLen<T>(arg:T):T{
    console.info(arg.length) // Error: T doesn't have .length
    return arg;
}

报错的原因是:T代表任意类型,所以使用这个函数的人可能传入boolean类型,而boolean类型是没有length属性的
改成:

function echoWithArr<T>(arg:T[]):T[]{
    console.info(arg.length) 
    return arg;
}
const arr = echoWithArr([1,2,3])
const arr2 = echoWithArr([1,2,"3"]) //T变成了number|string
// const arr3 = echoWithArr('str') //string实际上也有length属性,却会报错,所以这种定义方式有问题

采用interface extends

interface IWithLength {
  length:number
}
function echoWithArr2<T extends IWithLength>(arg:T):T {
  return arg
}
const arrWith = echoWithArr2([1,2,3])
const arrWith2 = echoWithArr2('str')
// const arrWith3 = echoWithArr2(23)  //不可以,因为number没有length属性

泛型在对象上的使用

interface KeyPair<T,U> {
  key:T,
  value:U
}
let kp1:KeyPair<number,string> = {key:23,value:"23"}
let kp2:KeyPair<string,number> = {key:"23",value:23}

Array<类型>就是这种用法

let n:number[] = [2,3,4]
let n2:Array<number> = [2,3,4]

泛型在函数上的使用

interface IPlus2<T> {
  (a:T,b:T):T
}
function plus2(a:number,b:number) :number {
  return a+b;
}
function connect(a:string,b:string) :string {
  return a+b;
}
const p2:IPlus2<number> = plus2
const c2:IPlus2<string> = connect

泛型在类上的使用

不使用typescript

class Queue {
  private data = []
  push(item) {
    return this.data.push(item)
  }
  pop(){
    return this.data.shift()
  }
}
const queue = new Queue()
queue.push(1)
queue.push("str")
console.info(queue.pop().toFixed())
// console.info(queue.pop().toFixed()) //运行时才会报错

使用typescript基本类型校验

class Queue {
  private data = []
  push(item:number) {
    return this.data.push(item)
  }
  pop():number{
    return this.data.shift()
  }
}
const queue = new Queue()
queue.push(1)
// queue.push("str") //会报错,起到了限制作用
console.info(queue.pop().toFixed())

使用泛型类

上面的起到了只允许传入number类型,但如果要弄个允许加入字符串,就得重新定义个类,这不合理

class Queue<T> { 
  private data = []
  push(item:T) {
    return this.data.push(item)
  }
  pop():T{
    return this.data.shift()
  }
}
const queue = new Queue<number>()
queue.push(3)
console.info(queue.pop().toFixed()) //3
const queueStr = new Queue<string>()
queueStr.push("3")
console.info(queueStr.pop().indexOf("3")) //0