阮一峰《TypeScript 教程》学习笔记——运算符
1. 一段话总结
TypeScript 提供多种类型运算符,用于对已有类型进行计算生成新类型,核心包括keyof(提取对象键名组成联合类型)、in(遍历联合类型成员)、方括号运算符(提取对象键值类型)、extends…?:(条件判断类型,子类型则返回X否则Y)、infer(在条件类型中推断泛型参数)、is(描述函数返回值与参数的类型关系,实现类型保护)、模板字符串类型(引用特定类型构建新字符串类型);这些运算符需结合泛型、联合类型等使用,可实现属性映射、类型提取、条件分支等复杂类型逻辑。
2. 思维导图

3. 详细总结
一、keyof 运算符
| 核心维度 | 说明 | 示例代码 |
|---|---|---|
| 作用 | 接受对象/类型作为参数,返回其所有键名组成的联合类型 | `type MyObj = {foo: number; bar: string}; type Keys = keyof MyObj; // ‘foo’ |
| 语法 | keyof 目标类型 | - |
| 特殊场景处理 | 1. 数组/元组:返回 `number | 索引字符串 |
| 核心用途 | 1. 精确关联函数参数与返回值类型(如 function prop<O,K extends keyof O>(obj:O, key:K):O[K]);2. 实现属性映射(如去除只读/可选属性) | type Mutable<O> = { -readonly [P in keyof O]: O[P] }; // 去除只读属性 |
二、in 运算符
| 核心维度 | 说明 | 示例代码 |
|---|---|---|
| 作用 | 在TypeScript中用于遍历联合类型的每个成员,生成对象类型 | `type U=‘a’ |
| 语法 | [变量名 in 联合类型]: 目标类型 | - |
| 与JavaScript差异 | ||
| 环境 | 作用 | 示例 |
| JavaScript | 判断对象是否包含某属性(返回布尔值) | 'a' in {a:1} → true |
| TypeScript | 遍历联合类型成员,构建对象类型 | `type U=‘a’ |
三、方括号运算符
| 核心维度 | 说明 | 示例代码 |
|---|---|---|
| 作用 | 提取对象指定键名的键值类型,支持联合键名 | type Person={age:number;name:string}; type Age=Person['age']; // number |
| 语法 | 目标类型[键名/键名联合类型] | - |
| 特殊场景 | 1. 联合键名:返回对应键值的联合类型(如 `Person[‘age’ | ‘name’] → number |
四、extends…?: 条件运算符
| 核心维度 | 说明 | 示例代码 |
|---|---|---|
| 作用 | 判断类型 T 是否为 U 的子类型,是则返回 X,否则返回 Y | type T=1 extends number ? true : false; // true |
| 语法 | T extends U ? X : Y | - |
| 联合类型处理 | 自动展开联合类型,每个成员单独判断后返回联合结果(`(A | B)extends U → (A→X/Y) |
| 避免展开 | 将 T 和 U 用方括号包裹([T] extends [U]),联合类型不展开 | `type ToArray=[T] extends [any] ? T[] : never; type T=ToArray<string |
| 嵌套使用 | 支持多重条件判断,实现复杂类型分支 | type LiteralTypeName<T>=T extends undefined?'undefined':T extends null?'null':never; |
五、infer 关键字
| 核心维度 | 说明 | 示例代码 |
|---|---|---|
| 作用 | 在条件类型中推断泛型参数(无需外部传入,从当前类型信息推导) | type Flatten<T>=T extends Array<infer Item> ? Item : T; |
| 语法 | T extends 父类型<infer 推断参数> ? 推断参数 : T | - |
| 适用场景 | 1. 提取数组成员类型(如 Flatten<string[]> → string);2. 提取函数参数/返回值(如 type Return<T>=T extends (...args:infer A)=>infer R ? R : T);3. 提取对象属性类型(如 type ExtractProp<T>=T extends {a:infer M,b:infer N} ? [M,N] : never) | type Func=(x:number)=>string; type FuncReturn=Return<Func>; // string |
六、is 运算符
| 核心维度 | 说明 | 示例代码 |
|---|---|---|
| 作用 | 描述函数返回值与参数的类型关系,实现类型保护(缩小变量类型范围) | `function isFish(pet:Fish |
| 语法 | 函数返回值类型 → 参数名 is 目标类型 | - |
| 适用场景 | 1. 判断参数类型(如 if(isFish(x)) x.swim();,确保x为Fish类型);2. 类方法返回值(用 this is T 描述当前实例类型) | class Student { isStudent():this is Student { return true; } } |
七、模板字符串类型
| 核心维度 | 说明 | 示例代码 |
|---|---|---|
| 作用 | 引用特定类型构建新字符串类型,支持展开联合类型 | type World="world"; type Greeting=hello ${World}; // "hello world" |
| 语法 | `字符串片段${引用类型}字符串片段` | - |
| 引用限制 | 仅支持引用 6种类型:string、number、bigint、boolean、null、undefined,其他类型(如对象)会报错 | type Obj={n:123}; type T=${Obj}; // 报错 |
| 联合类型展开 | 引用多个联合类型时,会交叉展开生成所有组合(如 `T=‘A’ | ‘B’; U=‘1’ |
4. 关键问题
问题1:TypeScript 中 keyof 与 in 运算符的核心差异是什么?分别适用于哪些场景?
答案:
两者核心差异体现在“作用对象”和“功能目标”,适用场景完全不同:
- 核心差异:
维度 keyof运算符in运算符作用对象 对象/类型(如接口、type别名) 联合类型(如 `‘a’ 核心功能 提取对象的键名,生成联合类型 遍历联合类型成员,生成对象类型 语法形式 keyof 目标类型[变量名 in 联合类型]: 类型 - 适用场景:
keyof:适用于“需要获取对象所有键名”的场景,如精确关联函数参数与返回值(如function prop<O,K extends keyof O>(obj:O, key:K):O[K])、实现属性映射(如去除只读属性);in:适用于“需要基于联合类型构建对象”的场景,如将联合类型成员转为对象属性(如type U='a'|'b'; type Foo={ [P in U]: number })、批量生成属性类型。
问题2:extends…?: 条件运算符处理联合类型时会自动展开,如何理解这一行为?若需避免展开,有什么方法?请结合示例说明。
答案:
-
自动展开行为:
当extends…?:的左侧类型为联合类型(如A|B)时,TypeScript 会将联合类型“拆分为单个成员”,分别判断每个成员是否为右侧类型的子类型,最终将结果组合为新的联合类型,即:
(A|B) extends U ? X : Y ≡ (A extends U ? X : Y) | (B extends U ? X : Y)
示例:type ToArray<T> = T extends any ? T[] : never; type Result = ToArray<string|number>; // string[]|number[](联合类型展开) -
避免展开的方法:
将条件运算符中extends两侧的类型用方括号包裹(如[T] extends [U]),此时 TypeScript 会将联合类型视为“单个整体”,不进行拆分,示例:type ToArray<T> = [T] extends [any] ? T[] : never; type Result = ToArray<string|number>; // (string|number)[](未展开) -
适用场景:
- 需“分别处理联合类型成员”时保留展开(如批量将每个成员转为数组);
- 需“将联合类型作为整体”时避免展开(如将联合类型整体转为数组)。
问题3:infer 关键字的核心作用是什么?如何利用它提取函数的参数类型和返回值类型?请结合示例说明。
答案:
-
核心作用:
infer用于在 条件类型 中“自动推断泛型参数”,无需从外部传入,仅需从当前类型信息(如数组、函数、对象)中推导,简化复杂类型提取逻辑。 -
提取函数参数与返回值类型:
利用infer在条件类型中匹配函数类型,分别推断“参数列表类型”和“返回值类型”,语法为:
type 类型别名<T> = T extends (...args: infer 参数类型) => infer 返回值类型 ? [参数类型, 返回值类型] : T
示例:// 提取函数的参数类型和返回值类型 type ExtractFunc<T> = T extends (...args: infer Args) => infer Return ? { args: Args; return: Return } : never;// 测试:函数 (x:number, y:string)=>boolean type Func = (x:number, y:string)=>boolean; type FuncInfo = ExtractFunc<Func>; // 结果:{ args: [x:number, y:string]; return: boolean } -
优势:
若不使用infer,需手动传入参数类型和返回值类型(如type ExtractFunc<T, Args, Return> = T extends (...args:Args)=>Return ? ... : ...),而infer可自动推导,减少冗余代码。
