TypeScript | 泛型(Generic)

267 阅读3分钟

泛型是指在定义函数、接口或者类时,未指定其参数类型,只有在运行时传入才能确定。那么此时的参数类型就是一个变量,通常用大写字母 T 来表示,当然你也可以使用其他字符,如:U、K等。

语法:在函数名、接口名或者类名添加后缀 :

function generic<T>() {}
interface Generic<T> {}
class Generic<T> {}

初识泛型

之所以使用泛型,是因为它帮助我们为不同类型的输入,复用相同的代码

比如写一个最简单的函数,这个函数会返回任何传入它的值:

//如果传入的是 number 类型:
function identity(arg: number): number {
    return arg
}

//如果传入的是 string 类型:
function identity(arg: string): string {
    return arg
}

通过泛型,可以把两个函数统一起来:

//添加类型变量T,T捕获用户传入的类型(比如:number),T当做返回值类型
function identity<T>(arg: T): T {
  return arg
}

泛型函数的返回值类型是根据你的业务需求决定,并非一定要返回泛型类型 T:

//入参的类型是未知的,但是通过 String 转换,返回字符串类型
function identity<T>(arg: T): string {
  return String(arg)
}

多个类型参数

泛型函数可以定义多个类型参数,其语法为通过逗号分隔 <T, U, K>

function extend<T, U>(first: T, second: U): T & U {
  for(const key in second) {
    (first as T & U)[key] = second[key] as any
  }
  return first as T & U
}

泛型参数默认类型

函数参数可以定义默认值,泛型参数同样可以定义默认类型

默认参数语法为<T = 默认类型>

function min<T = number>(arr:T[]): T{
  let min = arr[0]
  arr.forEach((value)=>{
     if(value < min) {
         min = value
     }
  })
   return min
}
console.log(min([20, 6, 8n])) // 6

泛型类型与泛型接口

//函数类型:
//等号左侧的 (x: number, y: number) => string 为函数类型
const add: (x: number, y: number) => string = function(x: number, y: number): string {
  return (x + y).toString()
}

//泛型类型:
function identity<T>(arg: T): T {
  return arg
}
//等号左侧的 <T>(arg: T) => T 即为泛型类型
let myIdentity: <T>(arg: T) => T = identity
//调用签名的对象字面量书写方式:{ <T>(arg: T): T }
let myIdentity: { <T>(arg: T): T } = identity

泛型接口:

interface GenericIdentityFn {
  <T>(arg: T): T
}
function identity<T>(arg: T): T {
  return arg
}
let myIdentity: GenericIdentityFn = identity

把泛型参数当作整个接口的一个参数,我们可以把泛型参数提前到接口名上。这样我们就能清楚的知道使用的具体是哪个泛型类型:

interface GenericIdentityFn<T> {
  (arg: T): T
}
function identity<T>(arg: T): T {
  return arg
}
let myIdentity: GenericIdentityFn<number> = identity

在使用泛型接口时,需要传入一个类型参数来指定泛型类型

泛型类

查找 number 类型的最小堆类:

class MinClass {
  public list: number[] = []
  add(num: number) {
    this.list.push(num)
  }
  min(): number {
    let minNum = this.list[0]
    for (let i = 0; i < this.list.length; i++) {
      if (minNum > this.list[i]) {
        minNum = this.list[i]
      }
    }
    return minNum
  }
}

需要支持字符串类型,需要泛型:

// 类名后加上 <T>
class MinClass<T> {
  public list: T[] = []
  add(num: T) {
    this.list.push(num)
  }
  min(): T {
    let minNum = this.list[0]
    for (let i = 0; i < this.list.length; i++) {
      if (minNum > this.list[i]) {
        minNum = this.list[i]
      }
    }
    return minNum
  }
}

let m = new MinClass<string>()
m.add('hello')
m.add('world')
m.add('generic')
console.log(m.min()) // generic

泛型约束

语法:通过 extends 关键字来实现泛型约束。

interface User {
  username: string
}
//约束了入参 user 必须包含 username 属性
function info<T extends User>(user: T): string {
  return 'imooc ' + user.username
}
type Args = number | string 
//约束了泛型参数 T 继承自类型 Args(由number和string组成的联合类型)
class MinClass<T extends Args> {}
const m = new MinClass<boolean>() // Error, 必须是 number | string 类型

多重类型泛型约束

通过 <T extends Interface1 & Interface2> 这种语法来实现多重类型的泛型约束:

interface Sentence {
  title: string,
  content: string
}

interface Music {
  url: string
}
//约束了泛型参数 T 需继承自交叉类型 Sentence & Music
//这样就能访问两个接口类型的参数
class Classic<T extends Sentence & Music> {
  private prop: T

  constructor(arg: T) {
    this.prop = arg
  }

  info() {
    return {
      url: this.prop.url,
      title: this.prop.title,
      content: this.prop.content
    }
  }
}

学习链接