返回

TypeScript 中的泛型精髓

前端

TypeScript 作为一门静态类型语言,通过泛型特性为开发者提供了强大的类型操作能力,不仅提高了代码的可读性和可维护性,还能够保证代码在运行时的类型安全。

1. 泛型基础

泛型在 TypeScript 中被广泛应用,从函数到类,泛型几乎无处不在。在泛型中,我们使用 <T> 来声明一个类型变量,它可以代表任意类型。

1.1 泛型函数

泛型函数是 TypeScript 最基本、最常用的泛型形式。通过声明 <T> 来代表函数中使用的类型参数,我们可以在函数中使用 T 来表示任意类型。例如:

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

这个泛型函数 identity() 可以接受任意类型的值作为参数,并返回相同类型的值。

1.2 泛型接口

泛型接口与泛型函数类似,但它允许我们为接口中的属性和方法指定类型参数。例如:

interface Stack<T> {
  push(value: T): void;
  pop(): T | undefined;
  peek(): T | undefined;
}

这个泛型接口 Stack 可以用来表示一个栈数据结构,我们可以使用任意类型的值作为栈中的元素。

1.3 泛型类

泛型类与泛型接口类似,但它允许我们在类中声明类型参数,并将其用于类的属性和方法。例如:

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

  push(value: T): void {
    this._values.push(value);
  }

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

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

这个泛型类 Stack 也可以用来表示一个栈数据结构,我们可以在类中使用 <T> 来表示栈中元素的类型。

2. 泛型高级应用

泛型除了基本用法外,还有许多高级的应用,例如:

2.1 类型约束

类型约束允许我们在泛型中指定类型参数的约束条件。例如,我们可以要求类型参数必须是某个类的子类,或者必须实现某个接口。例如:

function isNumber<T extends number>(value: T): boolean {
  return typeof value === "number";
}

这个泛型函数 isNumber() 要求类型参数 T 必须是 number 的子类,即只能是数字类型。

2.2 泛型推断

泛型推断是一种 TypeScript 的语法糖,它允许我们在使用泛型函数、接口和类时省略类型参数。例如,我们可以直接写:

const stack = new Stack<number>();

TypeScript 会自动推断出 <number> 这个类型参数。

3. 结束语

泛型是 TypeScript 中强大的特性,它可以帮助我们编写出更灵活、更可扩展的代码。通过掌握泛型的用法,我们可以显著提升 TypeScript 的开发效率和代码质量。