Skip to main content

TS <T> 函数泛型

一、泛型的定义

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

举个例子,一个函数接受一个 number 参数并返回一个 number 参数,写法如下:

function returnItem(para: number): number {
return para
}

如果想接受一个 string 类型,再返回 string 类型,写法如下:

function returnItem(para: string): string {
return para
}

以上编写方式存在代码重复度较高的问题,虽然可以用 any 类型替代,但目的是接收什么类型的参数返回什么类型的参数,即在运行时传参后才能确定类型,这时就可以使用泛型,如下:

function returnItem<T>(para: T): T {
return para
}

可以看到,泛型给予开发者创造灵活、可重用代码的能力。

二、泛型的使用

泛型通过 <> 的形式进行表述,可以声明:

  • 函数
  • 接口

1、函数声明

声明函数的形式如下:

function returnItem<T>(para: T): T {
return para
}

定义泛型的时候,可以一次定义多个类型参数,比如同时定义泛型 T 和 泛型 U

function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}

swap([7, 'seven']) // ['seven', 7]

2、接口声明

声明接口的形式如下:

interface ReturnItemFn<T> {
(para: T): T
}

当想传入一个 number 作为参数时,可以这样声明函数:

const returnItem: ReturnItemFn<number> = (para) => para

3、类声明

使用泛型声明类时,既可以作用于类本身,也可以作用于类的成员函数。

举个例子,实现一个元素同类型的栈结构:

class Stack<T> {
private arr: T[] = []

public push(item: T) {
this.arr.push(item)
}

public pop() {
this.arr.pop()
}
}

使用方式如下:

const stack = new Stack<number>()

如果上述只能传递 stringnumber 类型,可以使用 <T extends xx> 的方式猜实现约束泛型,如下所示:

泛型更高级的使用场景如下:

例如要设计一个函数,这个函数接受两个参数,一个参数为对象,另一个参数为对象上的属性,通过这两个参数返回这个属性的值;这时就涉及到泛型的索引类型和约束类型共同实现。

3、索引类型、约束类型

索引类型 keyof T 把传入对象的属性类型取出生成一个联合类型,这里的泛型 U 被约束在这个联合类型中,如下所示:

function getValue<T extends object, U extends keyof T>(obj: T, key: U) {
return obj[key] // ok
}

为什么需要使用泛型约束,而不是直接定义第一个参数为 object 类型呢?

因为默认情况 object 指的是 {},而接收的对象是各种各样的,一个泛型来表示传入的对象类型,比如 T extends object

使用如下图所示:

4、多类型约束

下面要实现两个接口的类型约束:

interface FirstInterface {
doSomething(): number
}

interface SecondInterface {
doSomethingElse(): string
}

可以创建一个接口继承上述两个接口,如下:

interface ChildInterface extends FirstInterface, SecondInterface {

}

正确使用如下:

class Demo<T extends ChildInterface> {
private genericProperty: T

constructor(genericProperty: T) {
this.genericProperty = genericProperty
}
useT() {
this.genericProperty.doSomething()
this.genericProperty.doSomethingElse()
}
}

通过泛型约束就可以达到多类型约束的目的。