跳到主要内容

TypeScript 泛型基础

介绍

TypeScript 泛型是一种强大的工具,它允许你编写灵活且可重用的代码。泛型的主要目的是在定义函数、类或接口时,不预先指定具体的类型,而是在使用时再指定类型。这样可以使代码更加通用,同时保持类型安全。

泛型的概念类似于函数参数,但它是针对类型的。通过使用泛型,你可以避免重复编写相似的代码,同时确保类型的一致性。

泛型的基本语法

泛型的基本语法非常简单。你可以通过在函数、类或接口的名称后面添加 <T> 来定义一个泛型类型。T 是一个占位符,代表任意类型。你可以使用任何字母或单词来代替 T,但通常使用 T 作为泛型类型的默认名称。

泛型函数

让我们从一个简单的泛型函数开始:

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

在这个例子中,identity 函数接受一个类型为 T 的参数 arg,并返回相同类型的值。你可以通过以下方式调用这个函数:

typescript
let output1 = identity<string>("Hello");
let output2 = identity<number>(42);

在第一个调用中,T 被推断为 string,而在第二个调用中,T 被推断为 number。TypeScript 会根据传入的参数自动推断类型,因此你也可以省略类型参数:

typescript
let output1 = identity("Hello"); // T 被推断为 string
let output2 = identity(42); // T 被推断为 number

泛型类

泛型不仅可以用于函数,还可以用于类。以下是一个简单的泛型类示例:

typescript
class Box<T> {
private value: T;

constructor(value: T) {
this.value = value;
}

getValue(): T {
return this.value;
}
}

let box1 = new Box<string>("Hello");
let box2 = new Box<number>(42);

console.log(box1.getValue()); // 输出: Hello
console.log(box2.getValue()); // 输出: 42

在这个例子中,Box 类接受一个泛型类型 T,并在构造函数中初始化一个 value 属性。通过使用泛型,Box 类可以存储任意类型的值。

泛型约束

有时你可能希望限制泛型类型可以接受的类型范围。你可以通过 extends 关键字来实现这一点。以下是一个使用泛型约束的示例:

typescript
interface Lengthwise {
length: number;
}

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

在这个例子中,loggingIdentity 函数接受一个泛型类型 T,但 T 必须实现 Lengthwise 接口。这意味着传入的参数必须具有 length 属性。

typescript
loggingIdentity("Hello"); // 输出: 5
loggingIdentity([1, 2, 3]); // 输出: 3
loggingIdentity({ length: 10, value: 42 }); // 输出: 10

实际应用场景

泛型在实际开发中有许多应用场景。以下是一些常见的例子:

1. 数据容器

泛型非常适合用于创建数据容器,如数组、栈、队列等。以下是一个简单的栈实现:

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

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

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

let stack = new Stack<number>();
stack.push(1);
stack.push(2);
console.log(stack.pop()); // 输出: 2
console.log(stack.pop()); // 输出: 1

2. 通用工具函数

泛型可以用于创建通用的工具函数,如 mapfilter 等。以下是一个简单的 map 函数实现:

typescript
function map<T, U>(array: T[], callback: (item: T) => U): U[] {
return array.map(callback);
}

let numbers = [1, 2, 3];
let doubled = map(numbers, (n) => n * 2);
console.log(doubled); // 输出: [2, 4, 6]

3. 类型安全的 API 响应

在处理 API 响应时,泛型可以帮助你确保类型安全。以下是一个简单的示例:

typescript
interface ApiResponse<T> {
data: T;
status: number;
}

function fetchData<T>(url: string): ApiResponse<T> {
// 模拟 API 调用
return {
data: {} as T,
status: 200
};
}

let response = fetchData<{ name: string }>("/api/user");
console.log(response.data.name); // 输出: undefined (模拟数据)

总结

TypeScript 泛型是一种强大的工具,它允许你编写灵活且可重用的代码。通过使用泛型,你可以避免重复编写相似的代码,同时确保类型的安全性。泛型可以用于函数、类、接口等多种场景,并且可以通过泛型约束来限制类型的范围。

附加资源

练习

  1. 编写一个泛型函数 swap,它接受两个参数并交换它们的值。
  2. 创建一个泛型类 Queue,实现队列的基本操作(入队、出队)。
  3. 使用泛型约束,编写一个函数 longest,它接受两个具有 length 属性的对象,并返回较长的那个。
提示

在练习中,尝试使用不同的类型来测试你的泛型代码,确保它们能够正确处理各种类型的数据。