TypeScript 泛型(Generics)详解
泛型(Generics) 是 TypeScript 最强大、最核心的特性之一。它允许你创建可重用、类型安全的组件,在不牺牲类型检查的前提下,让函数、类、接口等能够处理多种类型,而不是固定为某种特定类型。
泛型的核心思想:“类型变量” —— 用占位符(如 <T>)表示类型,在使用时再指定具体类型。
1. 泛型函数(Generic Functions)
基础示例:身份函数(Identity Function)
// 普通函数(无泛型)
function identity(arg: number): number {
return arg;
}
// 使用 any(失去类型安全)
function identityAny(arg: any): any {
return arg;
}
// let output: string = identityAny(123); // 不会报错,但不安全
// 泛型版本(推荐)
function identity<T>(arg: T): T {
return arg;
}
// 使用时指定类型
let numOutput: number = identity<number>(123);
let strOutput: string = identity<string>("hello");
// 或依靠类型推断(最常用)
let boolOutput = identity(true); // 类型自动推断为 boolean
多个泛型参数
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
let merged = merge({ name: "Alice" }, { age: 30 });
// merged 类型:{ name: string; } & { age: number; } → { name: string; age: number; }
2. 泛型接口(Generic Interfaces)
interface GenericArray<T> {
items: T[];
add(item: T): void;
get(index: number): T;
}
// 使用
let numberArray: GenericArray<number> = {
items: [1, 2, 3],
add(item) { this.items.push(item); },
get(index) { return this.items[index]; }
};
let stringArray: GenericArray<string> = {
items: ["a", "b"],
add(item) { this.items.push(item); },
get(index) { return this.items[index]; }
};
3. 泛型类(Generic Classes)
class Box<T> {
private content: T;
constructor(initial: T) {
this.content = initial;
}
set(value: T): void {
this.content = value;
}
get(): T {
return this.content;
}
}
// 使用
let numberBox = new Box<number>(100);
numberBox.set(200);
console.log(numberBox.get()); // 200
let stringBox = new Box<string>("hello");
stringBox.set("world");
// stringBox.set(123); // 错误:类型不匹配
4. 泛型约束(Constraints)—— 限制 T 的范围
默认情况下,T 可以是任何类型,有时需要限制它具有某些属性。
// 约束 T 必须有 length 属性
function printLength<T extends { length: number }>(arg: T): void {
console.log(arg.length);
}
printLength("hello"); // OK
printLength([1, 2, 3]); // OK
printLength({ length: 10, value: 5 }); // OK
// printLength(123); // 错误:number 没有 length
使用 keyof 约束键
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let person = { name: "Alice", age: 30 };
let name = getProperty(person, "name"); // 类型:string
let age = getProperty(person, "age"); // 类型:number
// getProperty(person, "email"); // 错误:email 不存在
5. 默认泛型参数(Default Type Parameters,TS 2.3+)
interface ApiResponse<T = any> {
data: T;
status: number;
}
// 使用时可省略
let response: ApiResponse = { data: "ok", status: 200 };
// 或指定
let jsonResponse: ApiResponse<string[]> = {
data: ["a", "b"],
status: 200
};
6. 常见泛型应用场景
| 场景 | 示例 |
|---|---|
| 数组工具函数 | function first<T>(arr: T[]): T |
| Promise | Promise<T>(TS 内置) |
| React 组件 | function List<T>(props: { items: T[] }) |
| 类型安全的工厂函数 | function create<T>(c: new () => T): T |
| 映射类型 | type Partial<T> = { [K in keyof T]?: T[K] } |
7. 内置泛型工具类型(Utility Types)
TypeScript 自带大量基于泛型的工具类型:
| 类型 | 作用 | 示例 |
|---|---|---|
Partial<T> | 所有属性可选 | Partial<User> |
Required<T> | 所有属性必选 | |
Readonly<T> | 所有属性只读 | |
Pick<T, K> | 挑选属性 | Pick<User, "name" | "age"> |
Omit<T, K> | 排除属性 | Omit<User, "password"> |
Record<K, T> | 创建键值对象类型 | Record<string, number> |
ReturnType<T> | 获取函数返回值类型 | ReturnType<typeof fetch> |
Parameters<T> | 获取函数参数元组 |
8. 最佳实践建议
| 建议 | 说明 |
|---|---|
| 优先让 TS 自动推断泛型 | 如 identity("hi") 而非 identity<string>("hi") |
| 使用约束避免过度宽松 | T extends { id: number } 等 |
泛型函数名后写 <T> | 保持一致性 |
| 避免泛型嵌套过深 | 可读性优先 |
| 结合接口/类使用泛型 | 构建可复用组件 |
| 多用内置工具类型 | 减少手动编写复杂类型 |
小结:泛型速查表
| 写法 | 含义 |
|---|---|
function fn<T>(arg: T): T | 基本泛型函数 |
T extends U | 约束 T 必须继承 U |
K extends keyof T | 键约束 |
class Box<T> | 泛型类 |
interface List<T> | 泛型接口 |
Promise<T> | 内置泛型示例 |
Partial<T> | 工具类型示例 |
泛型是 TypeScript 从“类型安全脚本语言”提升为“工业级类型系统”的关键。掌握泛型后,你可以编写高度可复用、类型严谨的库和应用(如 React、NestJS、RxJS 等都大量使用泛型)。
如果您想看更多实战示例(如泛型组件在 React 中的使用、高级条件泛型、泛型递归类型),或者需要一个完整的泛型工具库示例,请告诉我!