TypeScript类型映射详解:修饰符与键名重映射
本文深入解析TypeScript中的类型映射机制,涵盖基础映射语法、内置工具类型如Partial和Readonly的实现原理。重点讲解映射修饰符(+?、-?、+readonly、-readonly)对可选和只读属性的增删控制,以及TS4.1引入的键名重映射功能,包括通过模板字符串修改键名、属性过滤及联合类型映射的高级用法。
简介
类型映射是指依据特定规则将一种类型转换为另一种类型的过程,这一机制在对象类型的定义中尤为常见。
在此示例中,类型B通过索引类型对类型的属性进行遍历,从而完成了自身的定义。
type A = {
foo: number;
bar: number;
};
type B = {
[prop in keyof A]: string;
};
该操作实际上复制了一个与原类型结构一致的新类型,其中B完整保留了A的所有属性特征。
type A = {
foo: number;
bar: string;
};
type B = {
[prop in keyof A]: A[prop];
};
为了提升代码的复用性,开发者可以将常用的映射逻辑封装为泛型函数。
例如定义一个泛型,其功能是将传入对象的所有属性值统一转换为boolean类型。
type ToBoolean<Type> = {
[Property in keyof Type]: boolean;
};
除了联合类型,直接使用具体类型进行属性名映射同样可行。
type MyObj = {
[p in 'foo']: number;
};
// 等同于
type MyObj = {
foo: number;
};
在上述示例中,p in 'foo'被视为仅包含单个成员的联合类型,因此最终生成的对象类型也仅包含该单一属性。
利用映射机制,可以将对象的所有属性转换为可选属性。TypeScript内置的工具类型Partial<T>正是基于此原理实现。
type A = {
a: string;
b: number;
};
type B = {
[Prop in keyof A]?: A[Prop];
};在示例中,类型B在类型A的每个属性名后添加问号,从而使这些属性变为可选状态。
TypeScript内置的工具类型Readonly<T>负责将所有属性转换为只读属性,其底层实现同样依赖于映射机制。
// 将 T 的所有属性改为只读属性
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// 用法如下。
type T = { a: string; b: number };
type ReadonlyT = Readonly<T>;
// {
// readonly a: string;
// readonly b: number;
// }
映射修饰符
默认情况下,映射会保留原始对象中的可选属性和只读属性。若需移除这些特性,操作较为繁琐。为此,TypeScript引入了两个修饰符,用于在映射过程中添加或移除可选及只读属性。
- 使用
+修饰符,即编写+?或+readonly,可为映射后的属性添加?(可选)或readonly(只读)修饰符。 - 使用
–修饰符,即编写-?或-readonly,可从映射后的属性中移除?(可选)或readonly(只读)修饰符。
当涉及添加或移除可选属性时,+?或-?需置于属性名之后。
// 添加可选属性
type Optional<Type> = {
[Prop in keyof Type]+?: Type[Prop];
};
// 移除可选属性
type Concrete<Type> = {
[Prop in keyof Type]-?: Type[Prop];
};当涉及添加或移除只读属性时,+readonly和-readonly需置于属性名之前。
// 添加 readonly
type CreateImmutable<Type> = {
+readonly [Prop in keyof Type]: Type[Prop];
};
// 移除 readonly
type CreateMutable<Type> = {
-readonly [Prop in keyof Type]: Type[Prop];
};若需同时执行添加和移除?与readonly的操作,代码结构如下所示。
// 增加
type MyObj<T> = {
+readonly [P in keyof T]+?: T[P];
};
// 移除
type MyObj<T> = {
-readonly [P in keyof T]-?: T[P];
}TypeScript原生工具类型Required<T>专门用于移除可选属性,其实现依赖于-?修饰符。
此外,–?修饰符在移除可选属性后,该属性将不再允许赋值为undefined,从而成为必选属性。需要注意的是,此修饰符不会改变null类型的存在。
另外,+?修饰符可简写为?,而+readonly修饰符可简写为readonly。
键名重映射
语法
TypeScript 4.1引入了键名重映射功能,允许修改键名。具体做法是在键名映射后追加as关键字及新类型字句,新类型通常为模板字符串,用于对原始键名进行处理。
type A = {
foo: number;
bar: number;
};
type B = {
[p in keyof A as `${p}ID`]: number;
};
// 等同于
type B = {
fooID: number;
barID: number;
};在示例中,类型B是类型A的映射结果,但在映射过程中修改了属性名,即在原始属性名后拼接了字符串ID。
可见,键名重映射的语法结构为在键名映射后附加as + 新类型子句。这里的“新类型”通常是一个模板字符串,允许对原始键名执行各种操作。
属性过滤
键名重映射功能还支持对特定属性进行过滤。
联合类型的映射
由于键名重映射可以修改键名类型,因此原始键名的类型无需局限于string|number|symbol,任意联合类型均可用于键名重映射。
type S = {
kind: 'square',
x: number,
y: number,
};
type C = {
kind: 'circle',
radius: number,
};
type MyEvents<Events extends { kind: string }> = {
[E in Events as E['kind']]: (event: E) => void;
}
type Config = MyEvents<S|C>;
// 等同于
type Config = {
square: (event:S) => void;
circle: (event:C) => void;
}在示例中,原始键名的映射表达式为E in Events,其中Events是由两个对象组成的联合类型S|C。因此,E作为一个对象,通过键名重映射后,最终得到字符串键名E['kind']。
至此,关于TypeScript中类型映射的使用介绍完毕。如需了解更多TypeScript类型映射相关内容,请查阅本站往期文章或继续浏览后续文章,感谢大家的支持!
JS语法错误会阻止加载吗?区分SyntaxError与资源加载失败
2026-11-05 16:08:46
JavaScript常见SyntaxError语法错误排查与修复方法
2026-11-05 13:58:44
JS中slice、substring和substr截取字符串的区别
2026-11-05 13:43:47
JS 转 TS 手动迁移最佳实践:从配置 tsconfig 到消除 any 类型
2026-11-05 11:53:32
为什么JS数组push后得到的是数字而不是数组?
2026-11-05 09:59:18








