TypeScript 映射类型:{ [K in keyof T]: T[K] }
映射类型是 TypeScript 中一种基于旧类型创建新类型的方式。这就像是针对类型的 JavaScript Array.map() 方法:它遍历一个类型的所有属性,对其应用规则,并返回一个新的类型。
核心语法解析
理解 { [K in keyof T]: T[K] } 需要将其拆解为三个逻辑部分:
K in keyof T:这类似于for (const K in T)的循环。keyof T获取类型T的所有键名集合,K则代表其中的每一个键。[K]:这是新类型的属性名,默认情况下它直接使用了T的键名。T[K]:这是索引访问类型,表示获取T中键K对应的值类型。
步骤 1:创建基础映射类型
最基础的用法是创建一个结构与原类型完全相同的新类型。
- 定义 一个源接口
User:
interface User {
id: number;
name: string;
email: string;
}
- 编写 映射类型
UserClone:
type UserClone = {
[K in keyof User]: User[K];
};
- 验证 类型结果。此时
UserClone拥有与User完全相同的id、name和email属性,且类型保持一致。
步骤 2:添加属性修饰符
映射类型的真正威力在于遍历过程中添加修饰符,例如 readonly(只读)或 ?(可选)。
将所有属性设为只读
- 添加
readonly关键字到属性名前:
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
- 尝试 修改 属性。如果声明一个变量为
ReadonlyUser类型,TypeScript 将阻止你对其任何属性重新赋值。
将所有属性设为可选
- 添加 问号
?到属性名后:
type PartialUser = {
[K in keyof User]?: User[K];
};
- 赋值 空对象。对于
PartialUser类型的变量,const u: PartialUser = {}是合法的,因为所有属性都变成了可选的。
步骤 3:键重映射
使用 as 关键字可以重写键的名称。这在生成 Getters/Setters 或添加前缀时非常有用。
- 构建 一个为所有属性添加
get前缀的类型:
- 使用
Capitalize工具类型将首字母大写。 - 使用模板字面量类型拼接字符串。
type Getters = {
[K in keyof T as `get${Capitalize<K & string>}`]: () => T[K];
};
- 应用 该类型到
User:
type UserGetters = Getters<User>;
- 检查 结果类型结构。
UserGetters现在包含以下方法:
getId: () => numbergetName: () => stringgetEmail: () => string
步骤 4:值类型转换
通过修改 T[K] 部分,可以将属性值类型强制转换为另一种类型。
- 创建 将所有属性转换为
Promise的包装类型:
type Asyncify = {
[K in keyof T]: Promise;
};
- 观察 变化。
Asyncify<User>的name属性类型从string变成了Promise<string>。
步骤 5:实战构建“表单模型”类型
在开发中,后端返回的数据模型可能与前端表单输入组件(通常全是字符串)不一致。我们可以用映射类型自动生成表单类型。
- 定义 后端数据模型
ProductModel:
interface ProductModel {
id: number;
price: number;
isAvailable: boolean;
tags: string[];
}
- 编写 强制所有属性为字符串的映射类型:
type FormModel = {
[K in keyof T]: string;
};
- 生成 前端表单所需的类型:
type ProductForm = FormModel<ProductModel>;
- 对比 原始类型与表单类型的差异:
| 键名 | 原始类型 | 表单类型 | 说明 |
|---|---|---|---|
| id | number |
string |
表单输入框通常是字符串 |
| price | number |
string |
提交时需转换回数字 |
| isAvailable | boolean |
string |
复选框值通常为 "on"/"off" |
| tags | string[] |
string |
表单可能是逗号分隔的字符串 |
- 使用
ProductForm定义组件 Props。这样可以确保组件接收到的全是字符串类型,而底层数据结构依然由ProductModel维持。

暂无评论,快来抢沙发吧!