介绍
映射类型允许您通过迭代其键来转换现有对象类型。将它们视为 Array.prototype.map() 但处于 类型级别。语法 { [K in keyof T]: T[K] } 通过对 T 的每个属性应用转换来创建新类型。这就是 TypeScript 的许多内置实用程序类型在幕后工作的方式。
基本映射类型
最简单的映射类型创建现有类型的克隆:
type Clone<T> = { [K in keyof T]: T[K] };
interface User {
name: string;
age: number;
}
type UserClone = Clone<User>;
// { name: string; age: number }
添加修饰符
映射类型可以应用三个修饰符:readonly、?(可选)和 -(删除):
// Make all properties readonly
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };
// Make all properties optional
type MyPartial<T> = { [K in keyof T]?: T[K] };
// Remove readonly (the - modifier)
type MyMutable<T> = { -readonly [K in keyof T]: T[K] };
// Remove optional
type MyRequired<T> = { [K in keyof T]-?: T[K] };
这些正是内置 Readonly<T>、Partial<T> 和 Required<T> 的实现方式。
使用 as 键重新映射进行过滤
TypeScript 4.1 引入了使用 as 子句的键重新映射。这允许过滤或重命名键:
// Filter keys that are strings
type StringKeys<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface User {
name: string;
age: number;
email: string;
}
type OnlyStrings = StringKeys<User>;
// { name: string; email: string }
模板文字关键更改
将映射类型与 模板文字类型 结合起来以转换键名称:
// Add a prefix to all keys
type WithPrefix<T, P extends string> = {
[K in keyof T as `${P}_${K & string}`]: T[K];
};
interface User {
name: string;
age: number;
}
type Prefixed = WithPrefix<User, 'user'>;
// { user_name: string; user_age: number }
转换为 getter/setter 约定:
type Getters<T> = {
[K in keyof T as `get${Capitalize<K & string>}`]: () => T[K];
};
type UserGetters = Getters<{ name: string; age: number }>;
// { getName: () => string; getAge: () => number }
选择和省略实施
以下是 Pick 和 Omit 的内部工作原理:
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
type MyOmit<T, K extends keyof any> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
interface User {
name: string;
age: number;
email: string;
}
type NameOnly = MyPick<User, 'name'>;
// { name: string }
type NoEmail = MyOmit<User, 'email'>;
// { name: string; age: number }
实用图案
Deep Partial(递归映射类型)
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
可空属性
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
使用映射重命名键
type RenameKeys<T, M extends Record<string, string>> = {
[K in keyof T as K extends keyof M ? M[K] : K]: T[K];
};
结论
映射类型是 TypeScript 类型转换功能的支柱。从基本修饰符(readonly、?)到使用模板文字的高级键重新映射,它们使您可以对类型如何演变进行细粒度控制。与条件类型相结合,它们实现了编译时元编程,使 TypeScript 真正强大。

