FE-05-TypeScript 进阶:泛型、条件类型与类型体操
本节目标:掌握 TypeScript 的高级类型系统:泛型、条件类型、映射类型、模板字面量类型,能读懂和编写类型工具。
1. TS 基础回顾
1 | // 基本类型 |
2. 泛型(Generics)
核心思想:类型参数化,让函数/接口/类支持多种类型而不丢失类型信息。
2.1 泛型函数
1 | function identity<T>(value: T): T { |
2.2 泛型约束
1 | // 约束必须有 length 属性 |
2.3 多泛型参数
1 | function pair<K, V>(key: K, value: V): [K, V] { |
2.4 泛型接口
1 | interface ApiResponse<T> { |
2.5 泛型类
1 | class Stack<T> { |
2.6 默认类型参数
1 | interface Container<T = string> { |
3. 条件类型(Conditional Types)
1 | T extends U ? X : Y |
1 | type IsString<T> = T extends string ? true : false; |
3.1 infer 关键字
在条件类型中推断类型:
1 | // 提取函数返回值类型(手写 ReturnType) |
3.2 分布式条件类型
当 T 是联合类型时,条件类型会分发:
1 | type ToArray<T> = T extends any ? T[] : never; |
阻止分发:用 [] 包一层
1 | type ToArrayNonDist<T> = [T] extends [any] ? T[] : never; |
4. 映射类型(Mapped Types)
1 | type Readonly<T> = { |
4.1 keyof 操作符
1 | interface User { id: number; name: string; email: string; } |
4.2 内置映射类型
1 | // Partial<T> - 所有属性可选 |
4.3 高级映射
1 | // 映射并改名 |
5. 模板字面量类型
1 | type World = "world"; |
6. 类型守卫(Type Guards)
1 | function process(value: string | number) { |
7. 类型工具函数实战
7.1 DeepPartial
1 | type DeepPartial<T> = T extends object |
7.2 DeepReadonly
1 | type DeepReadonly<T> = T extends (infer U)[] |
7.3 NonNullable 深层
1 | type DeepNonNullable<T> = T extends null | undefined |
7.4 函数参数类型提取
1 | type MyParameters<T> = T extends (...args: infer P) => any ? P : never; |
8. 类型断言与转换
1 | // 类型断言(编译时,运行时无影响) |
9. 装饰器(实验性)
1 | // tsconfig: "experimentalDecorators": true |
10. 实战:类型安全的事件总线
1 | type EventMap = { |
小结
| 概念 | 关键点 |
|---|---|
| 泛型 | 类型参数化,保留类型信息 |
| 约束 | <T extends X> 限制 T 的范围 |
| 条件类型 | T extends U ? X : Y |
| infer | 在条件类型中提取子类型 |
| 映射类型 | { [K in keyof T]: ... } |
| 模板字面量 | 构造字符串联合类型 |
| 类型守卫 | 运行时检查 + 编译时收窄 |
| satisfies | 检查类型但保留具体信息 |
核心建议:
- 别为了用类型而用类型(项目先跑起来)
- 优先用内建工具类型(Partial、Pick、Omit、Record)
- 复杂类型推导不了时,用
as显式标注 - 用
// @ts-expect-error标注已知问题(不是忽略)
下一节讲 ES6+ 与现代 ECMAScript 演进。
- 本文作者: CoderSong
- 本文链接: https://jack-song-gif.github.io/2026/09/27/FE-05-TypeScript进阶/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!