TypeScript 泛型基础
介绍
TypeScript 泛型是一种强大的工具,它允许你编写灵活且可重用的代码。泛型的主要目的是在定义函数、类或接口时,不预先指定具体的类型,而是在使用时再指定类型。这样可以使代码更加通用,同时保持类型安全。
泛型的概念类似于函数参数,但它是针对类型的。通过使用泛型,你可以避免重复编写相似的代码,同时确保类型的一致性。
泛型的基本语法
泛型的基本语法非常简单。你可以通过在函数、类或接口的名称后面添加 <T>
来定义一个泛型类型。T
是一个占位符,代表任意类型。你可以使用任何字母或单词来代替 T
,但通常使用 T
作为泛型类型的默认名称。
泛型函数
让我们从一个简单的泛型函数开始:
function identity<T>(arg: T): T {
return arg;
}
在这个例子中,identity
函数接受一个类型为 T
的参数 arg
,并返回相同类型的值。你可以通过以下方式调用这个函数:
let output1 = identity<string>("Hello");
let output2 = identity<number>(42);
在第一个调用中,T
被推断为 string
,而在第二个调用中,T
被推断为 number
。TypeScript 会根据传入的参数自动推断类型,因此你也可以省略类型参数:
let output1 = identity("Hello"); // T 被推断为 string
let output2 = identity(42); // T 被推断为 number
泛型类
泛型不仅可以用于函数,还可以用于类。以下是一个简单的泛型类示例:
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
关键字来实现这一点。以下是一个使用泛型约束的示例:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在我们可以访问 length 属性
return arg;
}
在这个例子中,loggingIdentity
函数接受一个泛型类型 T
,但 T
必须实现 Lengthwise
接口。这意味着传入的参数必须具有 length
属性。
loggingIdentity("Hello"); // 输出: 5
loggingIdentity([1, 2, 3]); // 输出: 3
loggingIdentity({ length: 10, value: 42 }); // 输出: 10
实际应用场景
泛型在实际开发中有许多应用场景。以下是一些常见的例子:
1. 数据容器
泛型非常适合用于创建数据容器,如数组、栈、队列等。以下是一个简单的栈实现:
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. 通用工具函数
泛型可以用于创建通用的工具函数,如 map
、filter
等。以下是一个简单的 map
函数实现:
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 响应时,泛型可以帮助你确保类型安全。以下是一个简单的示例:
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 泛型是一种强大的工具,它允许你编写灵活且可重用的代码。通过使用泛型,你可以避免重复编写相似的代码,同时确保类型的安全性。泛型可以用于函数、类、接口等多种场景,并且可以通过泛型约束来限制类型的范围。
附加资源
练习
- 编写一个泛型函数
swap
,它接受两个参数并交换它们的值。 - 创建一个泛型类
Queue
,实现队列的基本操作(入队、出队)。 - 使用泛型约束,编写一个函数
longest
,它接受两个具有length
属性的对象,并返回较长的那个。
在练习中,尝试使用不同的类型来测试你的泛型代码,确保它们能够正确处理各种类型的数据。