TypeScript 的类型系统是图灵完备的——这意味着你可以在类型层面进行"编程"。掌握高级类型不仅能提高代码的可维护性,更是编写高质量库和框架的必备技能。本文从泛型出发,逐步深入到条件类型和类型体操。
泛型的本质:类型参数化
泛型(Generics)让函数、接口、类可以接受类型参数,实现"一次定义,多处复用":
// 没有泛型:每个类型都要写一遍\nfunction firstNumber(arr: number[]): number | undefined { return arr[0]; }\nfunction firstString(arr: string[]): string | undefined { return arr[0]; }\n// 使用泛型:一个函数适用所有类型\nfunction first(arr: T[]): T | undefined { return arr[0]; }\n\nconst num = first([1, 2, 3]); // num: number\nconst str = first(['a', 'b']); // str: string 泛型约束:限制类型参数的范围
// 使用 extends 约束泛型\ninterface HasLength { length: number; }\nfunction logLength(arg: T): T {\n console.log(arg.length); return arg;\n}\nlogLength('hello'); // string 有 length\nlogLength([1, 2, 3]); // array 有 length\nlogLength(123); // number 没有 length,编译错误\n\n// 使用 keyof 约束为对象属性的键\nfunction getProperty(obj: T, key: K): T[K] {\n return obj[key];\n} 条件类型:类型层面的 if/else
条件类型是 TypeScript 类型系统的"控制流":
// 基本语法:T extends U ? X : Y\ntype IsString = T extends string ? 'yes' : 'no';\ntype A = IsString; // 'yes'\ntype B = IsString; // 'no'\n\n// 实际应用:提取类型\ntype ExtractPromise = T extends Promise ? R : T;\ntype C = ExtractPromise>; // string\ntype D = ExtractPromise; // number\n\n// 深层提取\ntype DeepExtractPromise = T extends Promise ? DeepExtractPromise : T; infer 关键字:从类型中提取信息
// 提取函数返回值类型\ntype ReturnType = T extends (...args: any[]) => infer R ? R : never;\n// 提取函数参数类型\ntype FirstParam = T extends (first: infer F, ...rest: any[]) => any ? F : never;\n// 提取数组元素类型\ntype ArrayElement = T extends (infer E)[] ? E : never; 模板字面量类型:类型层面的字符串操作
type EventName = `on${Capitalize}`;\ntype ClickEvent = EventName<'click'>; // 'onClick'\ntype ChangeEvent = EventName<'change'>; // 'onChange'\n\n// 实战:类型安全的事件系统\ntype EventMap = { click: { x: number; y: number }; change: { value: string } };\ntype EventHandlers = {\n [K in keyof EventMap as `on${Capitalize}`]: (data: EventMap[K]) => void;\n};\n// { onClick: (data: { x: number; y: number }) => void;\n// onChange: (data: { value: string }) => void } 映射类型与工具类型实战
interface User {\n readonly id: number;\n name: string;\n age?: number;\n email: string;\n}\n// Partial:所有属性变为可选\ntype PartialUser = Partial;\n// Required:所有属性变为必填\ntype RequiredUser = Required;\n// Pick:选取部分属性\ntype UserPreview = Pick;\n// Omit:排除部分属性\ntype UserWithoutId = Omit;\n// Record:构造对象类型\ntype PageRoute = Record; 实战:类型安全的 API 请求封装
// 定义 API 接口映射\ninterface ApiMap {\n '/api/user': { id: number };\n '/api/users': { page: number; size: number };\n}\ninterface ApiResultMap {\n '/api/user': { id: number; name: string; email: string };\n '/api/users': { total: number; list: Array<{ id: number; name: string }> };\n}\n\n// 类型安全的请求函数\nasync function request(\n url: T, params: ApiMap[T]\n): Promise {\n const response = await fetch(url, { method: 'POST', body: JSON.stringify(params) });\n return response.json();\n}\n\n// 使用时获得完整的类型提示和检查\nconst user = await request('/api/user', { id: 1 }); // user.name 有自动补全 总结
TypeScript 的类型系统是一把双刃剑。用得好的类型能成为最好的文档和最可靠的守卫;用得过度的类型会成为维护的负担。掌握高级类型的关键在于:先解决实际问题,再追求类型的优雅。
评论 (0)