Skip to content

TypeScript 泛型

泛型(Generics)是 TypeScript 中最强大的特性之一。它允许我们编写可重用的、灵活的组件(如函数、类或接口),这些组件可以处理多种数据类型,而不仅仅是单一的一种。这在保持类型安全的同时,极大地提高了代码的复用性。

为什么需要泛型?

想象一个简单的函数,它接收一个参数并直接返回它。

typescript
// 使用 any 类型,但会丢失类型信息
function identity_any(arg: any): any {
    return arg;
}

// 使用特定类型,但无法复用
function identity_number(arg: number): number {
    return arg;
}

使用 any 类型会导致函数失去类型安全,因为编译器不知道返回值的具体类型。而为每种类型都写一个函数又非常繁琐。泛型就是为了解决这个问题而生的。

创建泛型函数

我们可以创建一个泛型函数 identity,它适用于所有类型。

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

这里,<T> 是一个类型变量,它是一种特殊的变量,只用于表示类型而不是值。它捕获了用户传入的类型(比如 number),然后我们就可以在函数内部使用这个类型。

使用泛型函数

有两种方式来使用泛型函数:

  1. 显式传入类型参数

    typescript
    let output = identity<string>("myString"); // output 的类型是 string
  2. 类型推断: 编译器会根据传入的参数自动确定 T 的类型。

    typescript
    let output = identity("myString"); // 编译器推断 T 为 string,output 的类型是 string
    let numOutput = identity(123); // 编译器推断 T 为 number,numOutput 的类型是 number

泛型约束 (Generic Constraints)

有时候,我们希望泛型函数能处理一些具有特定属性的类型。例如,一个日志函数可能需要访问参数的 .length 属性。在这种情况下,我们可以使用泛型约束。

我们可以创建一个接口来描述约束条件,然后使用 extends 关键字来应用它。

typescript
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length); // 现在我们可以安全地访问 .length 属性
    return arg;
}

loggingIdentity("hello"); // OK, string 有 length 属性
loggingIdentity([1, 2, 3]); // OK, array 有 length 属性
// loggingIdentity(123); // Error: number 没有 length 属性
// loggingIdentity({ name: 'test' }); // Error: { name: string } 没有 length 属性

泛型接口

我们也可以创建泛型接口,让接口的成员类型可以灵活变化。

typescript
interface GenericIdentityFn<T> {
    (arg: T): T;
}

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

let myIdentity: GenericIdentityFn<number> = identity;

console.log(myIdentity(100)); // 100
// console.log(myIdentity("test")); // Error

泛型类

类也可以是泛型的。泛型类在类名后使用 <T> 来定义类型参数。

typescript
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

// string 类型的泛型类实例
let myGenericString = new GenericNumber<string>();
myGenericString.zeroValue = "";
myGenericString.add = function(x, y) { return x + y; };

console.log(myGenericString.add("hello", " world")); // "hello world"

// number 类型的泛型类实例
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

console.log(myGenericNumber.add(10, 20)); // 30

泛型是 TypeScript 类型系统中一个非常重要的部分,掌握它可以让你编写出更通用、更健壮、更具可重用性的代码。

本站内容仅供学习和研究使用。