TypeScript类型映射详解:修饰符与键名重映射

作者:小括号

时间:2026-10-31

浏览:551

本文深入解析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类型映射相关内容,请查阅本站往期文章或继续浏览后续文章,感谢大家的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章