泛型(Generics)是 TypeScript 的核心特性,它允许我们编写可复用、类型安全的代码,同时保持灵活性。以下是深度解析和实际应用指南:
本质:参数化类型,将类型作为变量传递,像函数参数一样动态指定。
想象普通函数的参数是 值的变量,而泛型的参数是 类型的变量
T
就是一个类型变量,调用时才确定具体类型
就像函数运行时才接收具体值一样,泛型在调用时接收具体类型
// 定义泛型函数
function identity(arg: T): T {
return arg;
}
// 使用
const output1 = identity("hello"); // 显式指定 T 为 string
const output2 = identity(42); // 自动推断 T 为 number
典型场景:处理多种类型输入但逻辑相同的函数。
// 返回数组第一项
function firstElement(arr: T[]): T | undefined {
return arr[0];
}
const str = firstElement(["a", "b"]); // T 推断为 string
const num = firstElement([1, 2]); // T 推断为 number
典型场景:定义可复用的数据结构模板。
interface ApiResponse {
code: number;
data: T;
message: string;
}
// 使用
type UserResponse = ApiResponse<{ name: string; age: number }>;
const res: UserResponse = {
code: 200,
data: { name: "Alice", age: 30 },
message: "success"
};
典型场景:可定制化的类实现。
class Box {
private content: T;
constructor(value: T) {
this.content = value;
}
getValue(): T {
return this.content;
}
}
const stringBox = new Box("hello");
const numberBox = new Box(42);
典型场景:限制泛型参数必须满足某些条件。
interface HasLength {
length: number;
}
function logLength(arg: T): void {
console.log(arg.length);
}
logLength("hello"); // OK,string 有 length 属性
logLength([1, 2]); // OK,数组有 length
logLength(42); // Error: number 没有 length
典型场景:提供类型默认值。
interface Pagination {
items: T[];
page: number;
}
const p1: Pagination = { items: ["a", "b"], page: 1 }; // T 默认为 string
const p2: Pagination = { items: [1, 2], page: 1 };
典型场景:根据输入类型动态决定输出类型。
type IsString = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString; // false
interface ApiResponse {
success: boolean;
data: T;
error?: string;
}
async function fetchUser(): Promise> {
const res = await axios.get("/user");
return res.data;
}
2
// 实现 Partial、Pick、Omit 等工具类型
type MyPartial = { [P in keyof T]?: T[P] };
type MyPick = { [P in K]: T[P] };
type MyOmit = Pick>;
避免过度泛型:只在真正需要灵活性的地方使用。
明确约束:用 extends
限制泛型范围,增强类型安全。
优先推断:尽量让 TypeScript 自动推断类型参数。
命名规范:泛型变量通常用 T
、U
、K
、V
等大写字母。
Q1: 泛型会影响运行时性能吗?
A: 不会,泛型是编译期特性,会在编译后被擦除。
Q2: 泛型和 any 的区别?
A: 泛型保留类型信息,any
完全放弃类型检查。泛型是类型安全的。
Q3: 如何解决泛型推断失败?
A: 显式指定类型参数或调整函数签名。