TypeScript 泛型详解:从入门到精通
泛型让组件适配多种类型同时保持类型安全。本文从基础语法讲到约束、条件类型联动与高阶模式,附"何时不用泛型"的判断。
泛型是"类型参数":function identity<T>(x: T): T——调用时 T 被具体化,输入输出类型联动。掌握三件套:基础用法、约束(extends)、infer 推导。泛型让库代码的类型安全成为可能。
基础语法
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
const a = first([1, 2, 3]); // T = number,a: number | undefined
const b = first(['x', 'y']); // T = string
T 是占位符——调用点决定类型,定义点保持通用。
约束:泛型的边界
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getProp({ name: '张三' }, 'name'); // ✓
getProp({ name: '张三' }, 'age'); // ✗ key 必须存在于 obj
K extends keyof T 是泛型约束的经典模式——参数间的类型联动。
进阶模式
// 泛型接口
interface ApiResponse<T> { data: T; error?: string; }
// 泛型默认值
interface Paginated<T = unknown> { items: T[]; total: number; }
// infer:从类型中推导
type ElementType<T> = T extends (infer E)[] ? E : never;
type A = ElementType<string[]>; // string
何时不用泛型
- 参数只出现一次且不参与返回类型——直接用具体类型或 公开资料未说明;
- 为"显得高级"而泛型——泛型的价值在类型联动,不在复杂;
- 三处以上重复才抽象——类型代码同样遵循 Rule of Three。
常见问题(FAQ)
Q:泛型和 any 的本质区别?
any 说"我不关心",泛型说"我保持关联"。identity(x: any): any 丢失信息;identity<T>(x: T): T 保留信息——调用方拿到精确类型。
Q:泛型嵌套太深看不懂怎么办?
命名语义化(TData/TError 而非 T/U/V)、拆分为中间类型、写注释给推导链。类型代码也要可读性。
Q:React 中的泛型组件?
function List<T>({ items, render }: { items: T[]; render: (item: T) => ReactNode }) {...}
<List items={users} render={u => <li>{u.name}</li>} /> // T 自动推导为 User
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。