news 2026/9/28 2:47:23

TypeScript 映射类型修饰符:readonly、可变性修饰与可选性修饰的完整实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TypeScript 映射类型修饰符:readonly、可变性修饰与可选性修饰的完整实战指南
  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载

本篇指南围绕 The Concise TypeScript Book(本仓库website/src/content/docs/pt-br/book/mapped-type-modifiers.md所讲解的章节)的核心主题展开:TypeScript 映射类型(Mapped Types)中的三类修饰符——readonly/+readonly(只读)、-readonly(取消只读)、?(可选)——是如何驱动类型转换的。读者学完后将掌握用映射类型修饰符构造只读、可变、可选类型的标准写法,理解其在Partial、Readonly等内置工具类型中的实现原理,并能在实际项目中安全地应用和扩展这些模式。

1. 从映射类型说起:修饰符的作用对象

要理解修饰符,首先要明确它们“修饰”的是什么。TypeScript 的映射类型允许你基于一个已有类型,用keyof取出其全部属性键,再逐一对每个属性进行变换,生成一个新类型。映射类型的骨架如下:

type MyMappedType<T> = { [P in keyof T]: T[P][]; };

这里[P in keyof T]就是“映射”动作本身:P遍历T的所有键,T[P]通过索引访问取得每个属性的原类型。上面的例子把每个属性的类型改成了“原类型的数组”。比如:

type MyType = { foo: string; bar: number; }; type MyNewType = MyMappedType<MyType>; const x: MyNewType = { foo: ['hello', 'world'], bar: [1, 2, 3], };

MyNewType与MyType表达同样的信息(键名不变),但每个值的形态被转换成了数组。映射类型修饰符正是在这个“逐属性变换”的骨架之上,额外叠加readonly、?等标记,对每个属性的可变性与可选性进行批量改写。想深入了解映射类型本身,可阅读本仓库的 mapped-types.md 章节。

2. 三大修饰符的语义

本节对应原文档的核心要点:readonly(含+readonly)、-readonly与?。它们的共同点是都写在映射类型的花括号内部、属性声明的位置上,作用于被遍历到的每一个属性。

修饰符写法位置作用
readonly/+readonly属性键之前将映射后的属性标记为只读,禁止重新赋值
-readonly属性键之前移除只读标记,使属性恢复可写(可变)
?属性键之后将属性标记为可选(可缺省)

说明:本文“属性键”指[P in keyof T]这段映射表达式本身,例如readonly [P in keyof T]: T[P]中readonly位于映射表达式之前。

需要特别区分的是:?修饰符在原文档中没有给出显式的-?写法(-?表示移除可选标记,是Required<T>工具类型的底层实现,属 TS 4.1 引入的能力)。以当前章节文档为准,我们聚焦?的“添加可选”语义。

3. 原文档代码示例:只读、可变、可选三种变换

原文档(mapped-type-modifiers.md)给出三个可直接运行的类型定义:

type ReadOnly<T> = { readonly [P in keyof T]: T[P] }; // 所有属性标记为只读 type Mutable<T> = { -readonly [P in keyof T]: T[P] }; // 所有属性标记为可变 type MyPartial<T> = { [P in keyof T]?: T[P] }; // 所有属性标记为可选

逐一拆解:

  • ReadOnly<T>:对T的每个属性都加上readonly,得到一个所有属性均不可重新赋值的类型。实际使用时:
type Person = { name: string; age: number }; type ReadonlyPerson = ReadOnly<Person>; const p: ReadonlyPerson = { name: 'Simon', age: 17 }; p.name = 'John'; // 编译错误:无法分配给只读属性 'name'
  • Mutable<T>:-readonly是“移除只读”的显式写法。它常用于把某个已被冻结(readonly)的类型重新开放为可写。即便输入类型本没有readonly属性,结果也保持一致(对非只读属性移除只读是无操作)。示例:
type FrozenConfig = { readonly host: string; readonly port: number }; type WritableConfig = Mutable<FrozenConfig>; // 结果类型:{ host: string; port: number }
  • MyPartial<T>:对所有属性加上?,等价于内置的Partial<T>。它常用于参数对象“全部可缺省”的场景:
type PartialPerson = MyPartial<Person>; // 结果类型:{ name?: string; age?: number } function updatePerson(p: PartialPerson) { /* 只更新传入的字段 */ }

4. 结合仓库源码的纵深剖析

4.1 与内置工具类型的关系

MyPartial<T>实际就是手写的Partial<T>;ReadOnly<T>即手写的Readonly<T>。在 TypeScript 的标准库中:

  • Partial<T>={ [P in keyof T]?: T[P] }
  • Readonly<T>={ readonly [P in keyof T]: T[P] }
  • Required<T>={ [P in keyof T]-?: T[P] }

也就是说,原文档的示例正是在实现这些官方工具类型。本仓库的 type-manipulation.md 章节对Partial<T>、Required<T>、Readonly<T>等工具有系统讲解,例如Partial<Person>展开为{ name?: string | undefined; age?: string | undefined },Required<Person>将可选属性重新置为必选,Readonly<Person>则禁止后续赋值(a.name = 'John'属非法操作),可对照阅读。

4.2 仓库内真实使用佐证

当前仓库(The Concise TypeScript Book 的官方网站与构建代码)在自身代码中广泛使用这些模式,可作为“只读+映射”的实际应用样本:

  • website/src/data/plusEdition.ts 中多处使用Readonly<Record<string, ...>>声明配置对象,例如LOCALE_BY_LANG、PLUS_EDITION_COVERS、AMAZON_DOMAINS_BY_REGION均为Readonly<Record<...>>类型。这展示了“只读修饰符批量作用于 Record 的所有属性”这一组合:Record<K, T>本身是{ [P in K]: T }的映射类型,外层再套Readonly<...>即为双重映射。
  • website/src/config/locales.ts 中export type Locale = keyof typeof locales;是keyof取键的典型用例——它正是映射类型[P in keyof T]中“键集合”的来源,与修饰符示例中的keyof T一脉相承。

此外,仓库对?可选属性的基础语义也有专门章节:optional-properties.md 展示了b?: number的可选声明及解构默认值写法({ a, b = 100 }: X) => a + b;readonly-properties.md 则说明了readonly只能防止重写、不能保证深层不可变性,并给出Readonly<{ a: number }>与只读索引签名readonly [index: number]: string的写法。这两章共同补全了修饰符的“单属性”语义基础。

5. 修饰符与映射类型其他能力的组合

映射类型修饰符不是孤立的,它与本仓库其他章节介绍的映射能力经常搭配使用:

  • 与索引访问类型组合:T[P]本质是索引访问,见 type-indexing.md 与 type-manipulation.md 中的Person['age']示例。修饰符只改变属性的标记(readonly/optional),不改变值的类型。
  • 与+/-显式符号组合:+readonly与readonly等价,属显式“添加”写法;-readonly是“移除”。对于?,TS 同样支持-?(移除可选),本仓库的Required<T>讲解即属此列。
  • 与键重映射(as)组合:[P in keyof T as NewKey]可同时改名键并施加修饰符,例如生成“每个字段名加Id后缀且只读”的类型。该能力结合本仓库 template-union-types.md 中的模板字面量联合(如`id-${Products}-${Status}`)可用于构造键名。
  • 与索引签名组合:readonly [index: number]: string这样的只读索引签名见 readonly-properties.md;索引签名的键类型可为string | number | symbol,见 index-signatures.md。

6. 常见陷阱与实战建议

  1. readonly不保证深层不可变:Readonly<T>只冻结顶层属性的重新赋值;若属性值是对象或数组,其内部仍可变。需要递归只读时应自行递归映射(可结合条件类型),或采用第三方 deep-readonly 方案。
  2. -readonly只在映射类型中合法:它不能用于普通的对象类型字面量(如{ -readonly a: number }是非法的),只能出现在[P in keyof T]这样的映射语境中。
  3. ?的级联效应:用MyPartial<T>得到的可选属性,其类型在严格模式下为T[P] | undefined;访问前需做空值判断,或配合解构默认值(见 optional-properties.md)。
  4. 优先复用内置工具类型:当目标就是“全部只读”“全部可选”“全部必选”时,直接用Readonly<T>、Partial<T>、Required<T>更符合惯例;自定义修饰符类型适合在需要“部分键”或“键重命名”等定制场景使用。
  5. 多修饰符可叠加:{ readonly [P in keyof T]?: T[P] }能同时得到“只读且可选”的属性;对既有类型再套Mutable可以“解冻”之前套上的Readonly。

7. 小结

映射类型修饰符是 TypeScript 类型体操中“批量改写属性标记”的利器:readonly/+readonly批量冻结,-readonly批量解冻,?批量转可选。原文档给出的三个手写类型(mapped-type-modifiers.md)与内置的Readonly<T>、Mutable(等价于移除Readonly)、Partial<T>一一对应,而本仓库的 type-manipulation.md、readonly-properties.md、optional-properties.md、mapped-types.md 各章节从不同角度印证了这些模式,website/src/data/plusEdition.ts 与 website/src/config/locales.ts 则展示了Readonly<Record<...>>与keyof在真实项目中的落地用法。掌握这三个修饰符,就掌握了在类型层面统一控制“只读性”与“可选性”的核心手段,可以进一步向键重映射、递归只读等高阶类型体操延伸。

  • 文档
  • 教程

【免费下载链接】typescript-book

The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.

项目地址:https://gitcode.com/gh_mirrors/typ/typescript-book
点击查看免费下载

相关推荐

上一篇:Docker一键部署:awesome-kotlin从本地docker-compose开发到生产环境的完整部署指南
下一篇:30分钟让吃灰的Amlogic盒子亮出桌面:Armbian LXDE/XFCE 桌面环境实战

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 2:44:50

Arm Development Studio安装激活全攻略:从下载到调试一站式实操指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/28 2:43:19

RGB-D目标跟踪实战:数据对齐、梯度回传与深度敏感区域优化

简介&#xff1a;这是一份面向计算机视觉初学者与进阶学习者的多模态目标跟踪实践项目&#xff0c;聚焦RGB与Depth双模态融合技术&#xff0c;适用于课程设计、毕业设计及工程实训等场景。项目基于Python实现&#xff0c;采用边缘引导的单目深度估计网络EG-BTS构建COCO2017 RGB…

作者头像 李华