TypeScript 08:条件类型、infer 与手写工具类型
选学进阶。建议先读第 03 课(泛型与 mapped type)——本课是它的续篇。
1. 条件类型:类型世界的三元表达式
type IsString<T> = T extends string ? '是字符串' : '不是字符串'
type A = IsString<string> // '是字符串'
type B = IsString<number> // '不是字符串'
和三元表达式 condition ? a : b 同构,条件是第 3 课的 extends(是否满足形状)。T 填进来的时候才求值——复印机回锅。
分发:T 是联合类型时,会「逐个成员过一遍,再把结果合并」:
type ToArray<T> = T extends unknown ? T[] : never
type R = ToArray<string | number> // string[] | number[](不是 (string | number)[]!)
内置的 Exclude / Extract / NonNullable 全是「分发 + 条件」造出来的。
2. infer:在匹配中「抓一个占位符」
extends 匹配形状时,infer X 表示「这个位置是什么都行,抓出来命名为 X」:
type ElementType<T> = T extends (infer E)[] ? E : never
type E1 = ElementType<string[]> // string:数组元素被抓出来
type Unpacked<T> = T extends Promise<infer V> ? V : T
type U1 = Unpacked<Promise<string>> // string:Promise 拆包
揭底:第 03 课用的 ReturnType,标准库原文就长这样——
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never
3. 模板字面量类型:类型里也能写模板字符串
type Greeting = `hello ${'world' | 'TS'}` // 'hello world' | 'hello TS'
内置四件套 Uppercase / Lowercase / Capitalize / Uncapitalize 配合联合类型最好用:
type Getters = `get${Capitalize<'name' | 'age'>}` // 'getName' | 'getAge'
联合类型进模板会自动做笛卡尔积;${number} 可以约束「带单位的字符串」:
type Method = 'get' | 'post'
type Path = '/users' | '/orders'
type Route = `${Method} ${Path}` // 4 种组合全部合法
type CssSize = `${number}px` // '120px' ✅ '120' ❌
典型场景:事件名(onXxx)、路由、CSS 值这类「字符串但有规律」的约束。
4. 手写工具类型:收官
三种手法合体(分发+条件、mapped、keyof),常用工具类型全部可以手写:
// Exclude 的原理:分发 + 条件类型——不匹配的成员变 never,自然消失
type MyExclude<T, U> = T extends U ? never : T
type Remain = MyExclude<'a' | 'b' | 'c', 'a'> // 'b' | 'c'
// Pick:mapped 遍历,但只遍历 K 指定的键(对比 MyPartial 遍历全部)
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
// Record:keyof any = string | number | symbol,即「所有合法键类型」
type MyRecord<K extends keyof any, V> = {
[P in K]: V
}
到这里,Partial / Pick / Record / Exclude / ReturnType 你都亲手写过一遍了——工具类型没有魔法,遇到没见过的泛型按「约束 → 条件 → infer → mapped」拆开读即可。
5. 高频报错速查
| 报错 | 人话 |
|---|---|
Type '"gender"' does not satisfy the constraint 'keyof User' | Pick 的 K 用了不存在的键 |
Type '"a"' is not assignable to type 'Remain' | Exclude 排除后的联合不含它 |
Type 'X' is not assignable to type 'never' | 条件类型两边没交集,或分发没按预期发生 |
814



被折叠的 条评论
为什么被折叠?



