返回

深入理解 TypeScript 泛型及其应用

前端

在软件工程中,创建一致且定义良好的 API 非常重要,同时还要考虑可重用性。组件不仅要能够支持当前的数据类型,还要能够支持未来的数据类型,这在创建大型系统时提供了极大的灵活性。像 C# 和 Java 这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型。

在 TypeScript 中,泛型是一种强大的特性,它允许我们创建通用的类型,而无需为每种类型编写单独的代码。泛型可以帮助我们编写更可重用、更灵活的代码,并减少重复代码的数量。

泛型变量

泛型变量就像普通的变量,但它们没有固定的类型。我们可以通过声明一个类型变量并用它来表示变量的类型来创建泛型变量。例如,我们可以声明一个名为 T 的类型变量,并用它来表示变量 x 的类型:

let x: T;

现在,x 可以是任何类型的变量,只要它符合我们对 T 的约束。例如,我们可以通过使用 number 来约束 T,这样 x 就只能是数字:

let x: number;

泛型函数

我们可以使用泛型变量来创建泛型函数。泛型函数可以接受任何类型作为参数,并返回任何类型的值。例如,我们可以创建一个名为 sum 的泛型函数,它接受两个参数,并将它们相加:

function sum<T>(a: T, b: T): T {
  return a + b;
}

现在,我们可以使用 sum 函数来对任何类型的数字进行相加:

const result1 = sum(1, 2); // result1 的类型为 number
const result2 = sum('a', 'b'); // result2 的类型为 string

泛型类

泛型类就像普通的类,但它们可以接受类型参数。类型参数可以用来指定类中某些成员的类型。例如,我们可以创建一个名为 List 的泛型类,它可以存储任何类型的元素:

class List<T> {
  private items: T[] = [];

  add(item: T) {
    this.items.push(item);
  }

  get(index: number): T {
    return this.items[index];
  }
}

现在,我们可以使用 List 类来创建任何类型的元素的列表:

const list1 = new List<number>();
list1.add(1);
list1.add(2);
list1.add(3);

const list2 = new List<string>();
list2.add('a');
list2.add('b');
list2.add('c');

泛型接口

泛型接口就像普通的接口,但它们可以接受类型参数。类型参数可以用来指定接口中某些成员的类型。例如,我们可以创建一个名为 Comparable 的泛型接口,它可以比较任何类型的两个值:

interface Comparable<T> {
  compareTo(other: T): number;
}

现在,我们可以使用 Comparable 接口来创建任何类型的元素的比较器:

class NumberComparator implements Comparable<number> {
  compareTo(other: number): number {
    return this - other;
  }
}

class StringComparator implements Comparable<string> {
  compareTo(other: string): number {
    return this.localeCompare(other);
  }
}

泛型约束

我们可以使用泛型约束来限制泛型变量或泛型参数的类型。例如,我们可以使用 extends 关键字来指定泛型变量或泛型参数必须继承自某个类或接口。例如,我们可以创建一个名为 Sortable 的泛型接口,它要求其参数必须继承自 Comparable 接口:

interface Sortable<T extends Comparable<T>> {
  sort(): void;
}

现在,我们可以使用 Sortable 接口来创建任何类型的元素的排序器:

class NumberSorter implements Sortable<number> {
  sort(): void {
    this.items.sort((a, b) => a - b);
  }
}

class StringSorter implements Sortable<string> {
  sort(): void {
    this.items.sort((a, b) => a.localeCompare(b));
  }
}

条件类型

条件类型允许我们根据类型的某些条件来创建新的类型。例如,我们可以创建一个名为 IsNumber 的条件类型,它可以检查一个类型是否为数字类型:

type IsNumber<T> = T extends number ? true : false;

现在,我们可以使用 IsNumber 条件类型来检查一个类型的数字类型:

type A = IsNumber<number>; // A 的类型为 true
type B = IsNumber<string>; // B 的类型为 false

类型推断

TypeScript 可以自动推断大多数类型的类型。例如,我们可以创建一个名为 sum 的函数,它接受两个参数并返回它们的和,而无需显式指定参数的类型:

function sum(a, b) {
  return a + b;
}

TypeScript 会自动推断 ab 的类型为 number,并返回一个 number 类型的和。

高级类型

TypeScript 支持多种高级类型,包括联合类型、交叉类型和元组类型。联合类型允许我们将多个类型组合成一个新的类型。例如,我们可以创建一个名为 NumberOrString 的联合类型,它可以是数字类型或字符串类型:

type NumberOrString = number | string;

现在,我们可以使用 NumberOrString 联合类型来声明变量:

let x: NumberOrString;
x = 1;
x = 'a';

交叉类型允许我们将多个类型组合成一个新的类型。例如,我们可以创建一个名为 Person 的交叉类型,它包含一个 name 属性和一个 age 属性:

type Person = {
  name: string;
  age: number;
};

现在,我们可以使用 Person 交叉类型来声明变量:

let person: Person;
person = { name: 'John', age: 30 };

元组类型允许我们将多个类型组合成一个有序的集合。例如,我们可以创建一个名为 Point 的元组类型,它包含一个 x 坐标和一个 y 坐标:

type Point = [number, number];

现在,我们可以使用 Point 元组类型来声明变量:

let point: Point;
point = [1, 2];

泛型编程

泛型编程是一种使用泛型的编程范式。泛型编程允许我们编写代码,这些代码可以处理不同类型的数据,而无需为每种类型编写单独的代码。泛型编程可以帮助我们编写更可重用、更灵活的代码,并减少重复代码的数量。

总结

泛型是 TypeScript 中的一项强大功能,它允许我们创建可重用且类型安全的代码。泛型可以帮助我们编写更灵活、更可维护的代码。