一、核心速查:5个最实用的类型体操模式
以下是 类型编程中最常用、最能解决实际问题的 5 个技巧。直接对照使用,无需翻阅文档。
| 技巧 | 适用场景 | 核心代码 |
|---|---|---|
| 1. 键值映射( Types) | 批量修改对象属性类型 | type <T> = { [P in keyof T]: T[P] } |
| 2. 条件类型( Types) | 根据输入类型动态输出 | type <T> = T ? true : false |
| 3. 模板字面量类型 | 拼接字符串字面量类型 | type = Hello ${} |
| 4. 递归类型 | 处理嵌套结构或数组 | type <T> = { [P in keyof T]: <T[P]> } |
| 5. 类型推断(infer) | 从复杂类型中提取部分 | type <T> = T (...args: any[]) => infer R ? R : never |
二、详细实战:每个技巧的完整用法
技巧1:键值映射( Types)
标准写法(参考 官方内置工具类型):
type <T> = {
[P in keyof T]?: T[P]
}
常见变体:
添加 :{ [P in keyof T]: T[P] }
过滤属性:{ [P in keyof T as P _${} ? never : P]: T[P] }(剔除下划线开头的键)
修改值类型:{ [P in keyof T]: T[P] ? : T[P] }
实战案例:将接口中所有 Date 类型转为字符串
type <T> = {
[K in keyof T]: T[K] Date ? : T[K]
}
User {
name:
birth: Date
}
type = <User> // { name: ; birth: }
技巧2:条件类型( Types)
基础语法:T U ? X : Y
分布式条件类型(当 T 是联合类型时自动分发):
type <T> = T any ? T[] : never
type = < | > // [] | [],不是 (|)[]
常见应用:
排除 null/:type <T> = T null | ? never : T
提取函数参数:type <T> = T (...args: infer P) => any ? P : never
官方定义参考: 手册中 <T, U> 的实现
type <T, U> = T U ? never : T
技巧3:模板字面量类型( Types)
能力:在类型层面拼接字符串,配合 infer 可以解析字符串。
基础:
type = on${<>} // "" | "" 等
type = Hello, ${}! // 所有以 Hello, 开头、! 结尾的字符串
解析字符串(使用 infer):
type <Url > =
Url ${infer }://${infer Rest}
? { : ; rest: Rest }
: never
type = <";>
// { : "https"; rest: "" }
实战:类型安全的 CSS 单位:
type Unit = "px" | "em" | "rem"
type <U Unit> = ${}${U}
type Width = <"px"> // "10px" | "20px" 等所有带 px 的数字字符串
技巧4:递归类型( Types)
重要限制: 递归深度默认上限约 1000 层,超出会报错 Type is deep。
标准模式:
type <T> = {
[P in keyof T]: <T[P]>
}
处理数组和元组:
type <T> = T (infer U)[]
? <U>[]
: <T>
实战:展平嵌套数组():
type <T> = T any[]
? T[] infer U
? U any[]
? <U>
: U
: never
: T
type Flat = <[1, [2, [3]]]> // 1 | 2 | 3
技巧5:类型推断(infer)
只能在条件类型的 子句中使用。
提取数组元素类型:
type <T> = T (infer U)[] ? U : never
type Item = <[]> //
提取 内部类型(类似内置 <T>):
type <T> = T <infer U> ? <U> : T
type = <<<>>> //
提取函数返回值(内置 <T> 简化版):
type <T> = T (...args: any[]) => infer R ? R : never
提取函数参数元组(内置 <T>):
type <T> = T (...args: infer P) => any ? P : never
三、常见错误与解决方案
| 错误信息 | 原因 | 解决方案 |
|---|---|---|
Type is deep and |
递归类型超出深度限制或无终止条件 | 增加基础条件(如 T never ? never);重构为非递归 |
find name 'infer' |
在条件类型外部使用 infer |
确保 infer 只在 T ... ? infer X ... 内部 |
Type 'T' is not to type '' |
模板字面量类型要求传入字符串字面量 | 使用 T 约束或 as const 断言 |
| 分布式条件类型产生意外联合类型 | 未用 [] 包裹泛型参数 |
用 [T] [U] 代替 T U 关闭分发 |
四、练习与验证
自测题(答案见文末):
1. 实现 Pick<T, K>(从 T 中选取 K 键)
2. 实现 Omit<T, K>(从 T 中排除 K 键)
3. 实现 <T, U, V>(给对象 T 添加键 U,类型 V)
4. 实现 <T>(递归地将所有属性变为可选)
答案:
// 1. Pick
type <T, K keyof T> = { [P in K]: T[P] }
// 2. Omit (基于 Pick 和 )
type <T, K keyof T> = Pick<T, <keyof T, K>>
// 3.
type <T, U , V> = T & { [P in U]: V }
// 4.
type <T> = {
[P in keyof T]?: T[P] ? <T[P]> : T[P]
}
五、权威参考资料
官方文档:Type
内置工具类型源码:lib.es5.d.ts(随 安装)
社区标准:type-(类型体操题库,遵循官方行为)

