TypeScript 类型映射讲解
TypeScript 的类型映射(Mapped Types)是一种高级类型操作工具,允许开发者基于现有类型动态生成新类型。
一、基础概念
什么是类型映射
类型映射通过遍历现有对象的键(属性名),对每个属性进行转换、生成新的类型。其语法基于索引签名扩展,结合keyof和泛型实现类型推导。
举例:
type OptionsFlags<T> = {[K in keyof T]: boolean;
};
此代码将任意对象类型T的所有属性值改为布尔类型。
二、核心语法与功能
1. 基本语法
- 遍历键集合:使用
[K in keyof T]
语法遍历T
的所有属性名。 - 值类型转换:对每个属性
K
的值进行重新定义,如T[K]
或转换后的类型。
举例:复制类型
type Original = { a: number; b: string };
type Copy = { [K in keyof Original]: Original[K] }; // 等同于 Original
2. 修饰符操作
类型映射可以修改属性的特性(可选性、只读性):
- 添加修饰符:使用
+?
或+readonly
(可简写为?
和readonly
) 。
- 移除修饰符:使用
-?
或-readonly
。
举例:移除可选性
type Concrete<T> = {[K in keyof T]-?: T[K];
};
type MaybeUser = { id?: string; name?: string };
type User = Concrete<MaybeUser>; // { id: string; name: string }
3. 键名重映射( Key Remapping )
通过 as 关键词重命名键,常结合模板字面量或工具类型(如 Capitalize)实现复杂转换。
举例:生成Getters类型
type Getters<T> = {[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person { name: string; age: number; }
type LazyPerson = Getters<Person>;
// { getName: () => string; getAge: () => number }
4. 属性过滤
通过条件类型过滤特定属性:
type Filter<T> = {[K in keyof T as T[K] extends string ? K : never]: string;
};
type User = { name: string; age: number };
type FilteredUser = Filter<User>; // { name: string }
5. 联合类型映射
可将联合类型转换为对象类型:
type Shape = { kind: "square"; x: number } | { kind: "circle"; radius: number };
type EventMap<T extends { kind: string }> = {[E in T as E["kind"]]: (event: E) => void;
};
type Config = EventMap<Shape>;
// { square: (e: { kind: "square"; x: number }) => void; circle: ... }
三、内置工具类型
TypeScript 提供了多个基于映射类型的工具类型,这里简单列举几个,想更详细了解可以去看我的前置文章《解读TypeScript 类型工具》。
Partial<T>
:所有属性变为可选。Required<T>
:移除属性可选性。Record<K, V>
:生成键为K
、值为V
的类型 。
举例:
type Partial<T> = {[K in keyof T]?: T[K];
};
四、实际场景应用
1. 动态生成类型适配器
例如将api响应转换为前端组件需要的格式:
type APIResponse = { id: number; title: string; content: string };
type UIComponentProps = {[K in keyof APIResponse as `data-${K}`]: APIResponse[K];
};
// 结果:{ data-id: number; data-title: string; data-content: string }
2. 统一权限管理
将配置类型自动转换为权限类型:
type AppConfig = { username: string; layout: string };
type AppPermissions = {[K in keyof AppConfig as `change${Capitalize<K>}`]: boolean;
};
// { changeUsername: boolean; changeLayout: boolean }
3. 第三方库类型扩展
为 DOM 元素添加自定义属性:
type ElementAttributes = { id: string; class: string };
type CustomElement<T> = {[K in keyof T as `data-${string & K}`]: T[K];
} & ElementAttributes;