TypeScript 映射类型是什么?如何实现 Partial 和 Readonly?
简化版
映射类型可以遍历一个类型的 key,并按规则生成新类型。Partial<T> 是把所有属性变成可选,Readonly<T> 是把所有属性变成只读,它们都基于 [K in keyof T] 实现。
详细版
核心写法:
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};
映射类型常用于批量转换属性修饰符和值类型。
面试重点是理解 keyof T 拿 key,K in ... 遍历 key,T[K] 取属性值类型。
完整版教学
一、映射类型解决重复改属性的问题
如果一个对象类型有 20 个字段,想把它们都变成可选,手写会很痛苦。 映射类型提供了类型层面的“遍历 key”能力。 它让我们可以基于旧类型批量生成新类型。
type User = {
id: number;
name: string;
active: boolean;
};
目标是得到:
id?: number
name?: string
active?: boolean
不用重复写 3 个字段,映射类型可以统一转换。
二、keyof、in、索引访问是三件套
映射类型的核心三件套是 keyof T、K in、T[K]。
keyof T 得到属性名联合。
K in keyof T 遍历这些属性名。
T[K] 拿到每个属性对应的值类型。
type Copy<T> = {
[K in keyof T]: T[K];
};
对 User 来说:
keyof User = 'id' | 'name' | 'active'
K 依次取每个 key
T[K] 取 number / string / boolean
这就是很多工具类型的基础。
三、Partial 的实现
Partial<T> 把所有属性改成可选。
它只是在映射属性名后加了 ? 修饰符。
值类型保持不变。
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
type PartialUser = MyPartial<User>;
| 原字段 | 转换后 |
|---|---|
id: number | id?: number |
name: string | name?: string |
active: boolean | active?: boolean |
表单编辑、PATCH 请求、配置覆盖都经常用 Partial。
但它是浅层的,嵌套对象不会自动深度可选。
四、Readonly 的实现
Readonly<T> 把所有属性加上 readonly。
这表示属性不能通过该类型引用被重新赋值。
它同样是浅层的。
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};
const user: MyReadonly<User> = {
id: 1,
name: 'Alice',
active: true,
};
// user.name = 'Bob'; // 报错
readonly 是类型层约束,不等于运行时冻结。
如果运行时代码绕过类型系统,仍可能修改对象。
五、修饰符可以添加也可以移除
映射类型支持 +?、-?、+readonly、-readonly。
+ 通常可省略。
- 表示移除修饰符。
type RequiredAgain<T> = {
[K in keyof T]-?: T[K];
};
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
流程如下:
遍历 key
-> 决定是否加 readonly
-> 决定是否加 ?
-> 决定值类型 T[K]
这让工具类型可以表达非常丰富的批量转换。
六、键重映射让映射类型更强
较新的 TypeScript 支持在映射类型中用 as 重映射 key。
例如把所有字段名前加 get,生成 getter 类型。
这类题在中高级面试中很常见。
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
记忆钩子:映射类型就是类型世界里的 for…in,
keyof给范围,T[K]取值,修饰符决定怎么改。
这也是很多高级工具类型的基础能力。
七、常见误区与追问
- 误区:映射类型会生成运行时代码。 它只存在于类型系统,编译后会被擦除。
- 误区:Partial 会深度可选。 内置
Partial是浅层转换,嵌套对象需要自定义 DeepPartial。 - 误区:Readonly 等于 Object.freeze。
Readonly是编译期约束,不是运行时冻结。 - 追问:
T[K]是什么? 它是索引访问类型,用 key 取对象类型上对应属性的值类型。 - 追问:如何移除 readonly? 在映射类型里使用
-readonly修饰符。 - 追问:如何改 key 名? 使用键重映射语法
[K in keyof T as NewKey]。
八、加强记忆
映射类型按公式记:[K in keyof T]: T[K]。要变可选就加 ?,要只读就加 readonly,要改名就用 as。面试时手写 Partial 和 Readonly,再补浅层限制和修饰符移除,基本就能覆盖核心考点。