第8章:类型系统进阶
映射类型(Mapped Types)
1. 什么是映射类型?
映射类型(Mapped Types)是 TypeScript 中一种强大的类型操作工具,它允许你基于现有类型通过某种规则转换其属性,生成新的类型。
核心思想:遍历已有类型的属性,并对每个属性应用相同的类型变换规则。
2. 基本语法
type MappedType<T> = {
[P in keyof T]: NewType;
};
keyof T:获取类型T的所有属性名的联合类型。P in:遍历联合类型中的每个属性名。NewType:对原属性类型T[P]的转换规则。
示例:将所有属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
// 等价于:
// {
// readonly name: string;
// readonly age: number;
// }
3. 内置映射类型
TypeScript 提供了多个内置映射类型,可直接使用:
a. Partial<T>
将所有属性变为可选:
type PartialUser = Partial<User>;
// { name?: string; age?: number }
b. Required<T>
将所有可选属性变为必选:
type RequiredUser = Required<PartialUser>;
// { name: string; age: number }
c. Pick<T, K extends keyof T>
从 T 中选择部分属性:
type NameOnly = Pick<User, 'name'>; // { name: string }
d. Record<K, T>
创建键为 K、值为 T 的新类型:
type UserMap = Record<string, User>;
// { [key: string]: User }
4. 自定义映射类型
通过结合条件类型和模板字面量类型,可以实现更复杂的转换:
示例:将属性名添加前缀
type AddPrefix<T, Prefix extends string> = {
[P in keyof T as `${Prefix}${Capitalize<string & P>}`]: T[P];
};
type PrefixedUser = AddPrefix<User, 'get'>;
// { getName: string; getAge: number }
5. 实际应用场景
- API 响应处理:将后端返回的
snake_case字段转换为camelCase。 - 表单验证:生成与表单字段对应的错误消息类型。
- 状态管理:在 Redux 中派生不可变状态类型。
6. 注意事项
- 映射类型会保留原属性的修饰符(如
readonly和?)。 - 可通过
as子句重映射键名(TypeScript 4.1+)。 - 避免过度嵌套映射类型,以免影响编译性能。
扩展阅读:
结合key remapping和template literal types可以实现更动态的键名转换,例如过滤特定属性或修改命名风格。
