返回

精解泛型在 TypeScript 中的本质和用法

前端

泛型概述

泛型允许你使用占位符来表示类型,这样你就可以在代码中使用这些占位符来表示任何类型的数据。例如,以下代码中的 T 就是一个占位符:

function identity<T>(x: T): T {
  return x;
}

这个 identity 函数接受任何类型的数据作为参数,并将该参数原样返回。因为我们使用了泛型,所以我们可以使用 identity 函数来处理任何类型的数据,而不需要为每种数据类型创建单独的函数。

泛型函数

泛型函数是接受泛型参数的函数。泛型参数允许你定义函数的通用行为,而不需要为每种数据类型创建单独的函数。例如,以下代码中的 map 函数是一个泛型函数:

function map<T, U>(array: T[], f: (x: T) => U): U[] {
  return array.map(f);
}

这个 map 函数接受一个数组和一个函数作为参数,并将该数组的每个元素映射到另一个值。因为我们使用了泛型,所以我们可以使用 map 函数来处理任何类型的数据,而不需要为每种数据类型创建单独的函数。

泛型接口

泛型接口是定义泛型类型的接口。泛型接口允许你创建可重用的类型,而无需为每种数据类型创建单独的类型。例如,以下代码中的 List 接口是一个泛型接口:

interface List<T> {
  add(x: T): void;
  remove(x: T): void;
  get(index: number): T;
  size(): number;
}

这个 List 接口定义了一个列表类型,该列表可以存储任何类型的数据。因为我们使用了泛型,所以我们可以使用 List 接口来创建任何类型的数据的列表,而不需要为每种数据类型创建单独的接口。

泛型类

泛型类是定义泛型类型的类。泛型类允许你创建可重用的类,而无需为每种数据类型创建单独的类。例如,以下代码中的 Stack 类是一个泛型类:

class Stack<T> {
  private _items: T[] = [];

  push(x: T): void {
    this._items.push(x);
  }

  pop(): T | undefined {
    return this._items.pop();
  }

  peek(): T | undefined {
    return this._items[this._items.length - 1];
  }

  size(): number {
    return this._items.length;
  }
}

这个 Stack 类定义了一个栈类型,该栈可以存储任何类型的数据。因为我们使用了泛型,所以我们可以使用 Stack 类来创建任何类型的数据的栈,而不需要为每种数据类型创建单独的类。

泛型的优势

泛型具有以下优势:

  • 代码重用:泛型可以让你创建可重用的组件,而无需为每种数据类型创建单独的组件。这可以节省大量的代码量,并使你的代码更容易维护。
  • 代码简洁:泛型可以使你的代码更加简洁。例如,以下代码使用泛型来定义一个 map 函数:
function map<T, U>(array: T[], f: (x: T) => U): U[] {
  return array.map(f);
}

这段代码比以下代码更加简洁:

function mapNumberArray(array: number[], f: (x: number) => number): number[] {
  return array.map(f);
}

function mapStringArray(array: string[], f: (x: string) => string): string[] {
  return array.map(f);
}
  • 代码灵活性:泛型可以使你的代码更加灵活。例如,你可以使用泛型来创建