TypeScript 类与泛型的使用实践记录
在 TypeScript 中,泛型是提升代码灵活性和可重用性的重要工具,特别适用于开发通用类、函数或数据结构。本文将探讨 TypeScript 泛型的基础用法、应用场景,以及结合类型约束提升代码安全性和表达力的技巧。
一、泛型的基本概念
泛型(Generics) 是一种在定义函数、接口或类时,不预先指定具体的类型,而是在使用时再指定的特性。泛型的核心思想是“类型参数化”,它能让代码更加灵活且类型安全。
二、泛型的使用方法
1. 泛型函数
泛型函数允许我们在定义时不指定参数的具体类型,而是在调用时动态传入类型参数。
示例:
function identity<T>(arg: T): T {
return arg;
}
// 调用时指定类型
const output1 = identity<string>("Hello, TypeScript");
// 类型推断
const output2 = identity(42); // 推断 T 为 number
应用场景:
• 处理返回值类型与参数类型一致的情况,如包装函数、数据处理等。
2. 泛型接口
泛型可以应用于接口,用于定义更加灵活的结构。
示例:
interface KeyValuePair<K, V> {
key: K;
value: V;
}
const pair: KeyValuePair<string, number> = { key: "age", value: 30 };
应用场景:
• 定义通用的数据结构,如键值对、树节点等。
3. 泛型类
泛型类用于定义支持多种数据类型的通用类。
示例:
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
}
const stringStack = new Stack<string>();
stringStack.push("TypeScript");
console.log(stringStack.pop()); // 输出 "TypeScript"
const numberStack = new Stack<number>();
numberStack.push(42);
console.log(numberStack.peek()); // 输出 42
应用场景:
• 创建通用容器类,如栈、队列、链表等。
4. 泛型约束
泛型默认可以接受任意类型,但有些场景需要对类型参数施加约束,以限制其范围。
使用 extends 定义约束:
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): void {
console.log(arg.length);
}
logLength("Hello"); // 字符串有 length 属性
logLength([1, 2, 3]); // 数组有 length 属性
// logLength(42); // 错误:number 没有 length 属性
应用场景:
• 需要使用特定属性或方法的类型时,如数组操作、对象操作等。
三、实际开发中的应用场景
1. 数据处理中的泛型
在处理复杂数据结构时,泛型可以确保数据类型一致性。
示例:通用的过滤函数
function filterArray<T>(arr: T[], predicate: (value: T) => boolean): T[] {
return arr.filter(predicate);
}
const numbers = [1, 2, 3, 4];
const evenNumbers = filterArray(numbers, (n) => n % 2 === 0);
console.log(evenNumbers); // [2, 4]
2. API 响应处理
在处理 RESTful API 响应时,泛型能动态指定返回数据类型。
示例:
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
return fetch(url)
.then(response => response.json());
}
// 使用示例
interface User {
id: number;
name: string;
}
fetchApi<User[]>('/api/users').then(response => {
console.log(response.data); // 推断为 User[]
});
3. React 中的泛型
在 React 中,泛型常用于定义组件的 props。
示例:
interface ListProps<T> {
items: T[];
renderItem: (item: T) => JSX.Element;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
// 使用 List 组件
interface User {
id: number;
name: string;
}
const users: User[] = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" }
];
<List
items={users}
renderItem={(user) => <span>{user.name}</span>}
/>;
四、类型约束提升代码安全性
结合联合类型和条件类型的约束:
泛型可以结合条件类型和联合类型,进一步增强灵活性。
示例:实现一个键值提取工具
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: "Alice" };
console.log(getProperty(user, "name")); // 输出 "Alice"
// console.log(getProperty(user, "age")); // 错误:'age' 不存在于类型 'user'
优势:
• 避免访问对象中不存在的属性。
• 提升开发过程中的类型安全性。
五、总结
TypeScript 泛型的核心在于提供灵活的类型参数化能力,适合于各种需要通用性和类型安全的场景。通过结合泛型函数、类、接口以及类型约束,可以实现高效且安全的代码。在实际开发中,合理应用泛型不仅能提升代码复用性,还能显著减少类型错误,提高开发效率和代码质量。