ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

The Concise TypeScript Book 精读:映射类型修饰符(Mapped Type Modifiers)全解

The Concise TypeScript Book 精读:映射类型修饰符(Mapped Type Modifiers)全解 文档教程【免费下载链接】typescript-bookThe Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.项目地址https://gitcode.com/gh_mirrors/typ/typescript-book点击查看免费下载映射类型修饰符是 TypeScript 在「映射类型」基础上提供的一组类型级开关让你在对既有类型逐属性变换时同步调整每条属性的只读性与可选性。本指南以开源仓库 The Concise TypeScript Book 的 es 版映射类型修饰符章节 为骨架结合仓库中映射类型、只读属性、可选属性等相邻章节与类型系统源码级说明带你从语法到实战完整掌握readonly、-readonly、?三种修饰符的用法、加减法语法、与内置工具类型的关系及其边界限制。读完你将能够自己写出ReadOnlyT、MutableT、MyPartialT这类可复用的类型工具并理解ReadonlyT、PartialT、RequiredT等标准库工具类型的底层实现原理。一、先理解载体映射类型Mapped Types修饰符本身不能单独使用它必须出现在映射类型的遍历语法之中。所谓映射类型就是基于一个既有类型通过keyof取出其全部键再逐一对每个键的取值类型进行变换最终产出一个全新类型。仓库的 映射类型章节 给出了最典型的骨架type MyMappedTypeT { [P in keyof T]: T[P][]; }; type MyType { foo: string; bar: number; }; type MyNewType MyMappedTypeMyType; const x: MyNewType { foo: [hello, world], bar: [1, 2, 3], };这里[P in keyof T]是遍历语法P依次取到T的每个键右侧T[P]索引访问类型取出该键对应的值类型再包装成数组。于是MyType的foo: string与bar: number被映射成foo: string[]与bar: number[]——信息不变形态改变这正是映射类型的核心价值。而映射类型修饰符则负责在遍历过程中额外控制每条属性的readonly只读与?可选属性。它们写在属性名之前、与[P in keyof T]同处一个位置type X { readonly [P in keyof T]: T[P] }; // 修饰符位于遍历语法之前二、三种核心修饰符语法与语义原文档将修饰符归纳为三类以下逐一展开。2.1readonly/readonly把属性变为只读在映射类型中为每条属性加上readonly即可让结果类型的全部属性不可重新赋值type ReadOnlyT { readonly [P in keyof T]: T[P] }; // All properties marked as read-onlyreadonly与readonly语义完全等价——表示「显式添加该修饰符」是冗余但更明确、可读性更好的写法type ReadOnlyT { readonly [P in keyof T]: T[P] }; // 与上一写法等价两者都是添加只读约束。若省略前缀直接写readonlyTypeScript 同样视为添加。2.2-readonly把属性变回可变-readonly是readonly的逆操作用于移除既有类型上的只读标记让属性恢复可写type MutableT { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable注意-只能配合修饰符使用如-readonly、-?单独写-本身没有意义。/-前缀的引入TypeScript 2.8 起使得修饰符不再是单向追加而是可加可删这也是实现RequiredT等工具类型的语法基础。2.3?把属性标记为可选在映射类型中加入?会让结果类型的所有属性变为可选即属性可以缺失type MyPartialT { [P in keyof T]?: T[P] }; // All properties marked as optional这里的?与普通对象类型中b?: number的语义一致——仓库的 可选属性章节 中特别说明可选属性可通过解构赋默认值来兜底type X { a: number; b?: number; }; const x ({ a, b 100 }: X) a b;同样地-?即-加?可以移除可选标记强制所有属性必填这正是内置RequiredT的做法type MyRequiredT { [P in keyof T]-?: T[P] }; // All properties marked as required三、原文档完整示例三种工具类型一次到位原文档用三个并排定义的泛型工具类型直观展示了三种修饰符的独立用法这是本主题最核心的实操片段完整保留如下type ReadOnlyT { readonly [P in keyof T]: T[P] }; // All properties marked as read-only type MutableT { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable type MyPartialT { [P in keyof T]?: T[P] }; // All properties marked as optional配合一个实际类型走一遍全流程type Person { readonly name: string; // 原本就是只读 age: number; }; // 1) 全部变成只读 type FrozenPerson ReadOnlyPerson; // { readonly name: string; readonly age: number; } // 2) 全部变回可变含原本 readonly 的 name type EditablePerson MutablePerson; // { name: string; age: number; } // 此时 name 可以被重新赋值 // 3) 全部变成可选 type PartialPerson MyPartialPerson; // { readonly name?: string; age?: number; }一个关键观察MutableT能同时把原本声明为readonly的属性解除只读这正是-readonly存在的原因——单靠readonly只能加不能减有了-前缀后映射类型才具备双向调整能力。四、组合使用同时控制只读与可选修饰符不是互斥的它们可以自由叠加在同一映射类型上形成四种组合// 全部只读 全部可选 type ReadonlyPartialT { readonly [P in keyof T]?: T[P] }; // 全部可变 全部可选等价于去掉 readonly 的 Partial type MutablePartialT { -readonly [P in keyof T]?: T[P] }; // 全部只读 全部必填 type ReadonlyRequiredT { readonly [P in keyof T]-?: T[P] }; // 全部可变 全部必填等价于 RequiredMutableT type MutableRequiredT { -readonly [P in keyof T]-?: T[P] };这种组合能力在实战中非常常见例如「接口返回的字段都是必填但前端修改表单时希望它们全部变为可选且可变」一条映射类型即可完成type ApiUser { readonly id: number; readonly name: string; readonly email: string; }; // 表单编辑态去掉只读、允许部分字段缺失 type EditableForm { -readonly [P in keyof ApiUser]?: ApiUser[P] }; // { id?: number; name?: string; email?: string; }五、与内置工具类型的对应关系你可能已经注意到上面手写的ReadOnlyT、MyPartialT与 TypeScript 标准库内置的ReadonlyT、PartialT几乎一模一样。事实上映射类型修饰符正是这些内置工具类型的底层实现机制。仓库的 类型操作章节 系统整理了这些内置工具类型的语义PartialT构造一个把T所有属性都设为可选的类型RequiredT构造一个把T所有属性都设为必填的类型ReadonlyT构造一个把T所有属性都设为只读的类型其效果与readonly [P in keyof T]完全一致。例如ReadonlyT的等价手写实现就是我们上面定义的ReadOnlyT而RequiredT对应{ [P in keyof T]-?: T[P] }。仓库 只读属性章节 还展示了readonly在普通类型与索引签名中的表现interface Y { readonly a: number; } type X { readonly a: number; }; type J Readonly{ a: number; }; type K { readonly [index: number]: string; // 只读索引签名 };需要特别强调的是J Readonly{ a: number }与手写{ readonly a: number }得到的类型结构完全相同——因为在结构类型系统下仓库 探索类型系统章节 有专述类型由结构决定而非声明方式决定所以「用内置工具」和「手写修饰符」在编译层面等价你可以按可读性任选其一。六、实战场景与注意事项6.1 从 API 响应派生「不可变」快照后端返回的领域对象往往在业务层不应被随意改写可以把它包装为只读版本作为跨模块传递的只读快照type ApiOrder { id: string; items: string[]; total: number; }; type ImmutableOrder ReadonlyApiOrder; // { readonly id: string; readonly items: string[]; readonly total: number; } function render(order: ImmutableOrder) { // order.total 0; // 编译错误Cannot assign to total because it is a read-only property }6.2 解除第三方库类型的只读约束当你拿到一个被ReadonlyT包裹的入参类型又需要在本地做变更时-readonly就是唯一的「解药」type LockedConfig Readonly{ host: string; port: number }; // 本地可变副本 type MutableConfig { -readonly [P in keyof LockedConfig]: LockedConfig[P] }; // { host: string; port: number }6.3 注意只读是类型层面的契约不是运行时保证原文档及 只读属性章节 都反复强调一个边界readonly只保证编译期不能重新赋值并不提供任何运行时不可变性不产生任何运行时效果。例如type Config Readonly{ retries: number }; const cfg: Config { retries: 3 }; // cfg.retries 5; // 编译错误 // 但类型断言可以绕过运行时并不阻止写入 (cfg as { retries: number }).retries 5; // 合法同理readonly是浅层的它只禁止对属性本身重新赋值不禁止修改属性内部的内容如数组元素、嵌套对象字段。若要深度不可变需要递归组合ReadonlyT或借助as const等额外手段这超出了修饰符本身的职责范围。6.4 可选属性与strictNullChecks的联动映射出可选属性后在访问时 TypeScript 会要求先做存在性判断仓库 strictnullchecks 章节 对此有专门论述。因此由?派生出的类型在读取字段时通常需要配合if (obj.field ! undefined)或可选链obj?.field这是使用MyPartialT类工具时的常见伴生约束。七、进阶延伸修饰符不是唯一变换维度掌握readonly、?、/-之后你会自然联想到一个更广泛的问题既然遍历[P in keyof T]可以修改属性特性是否也能修改键本身答案是肯定的——在更高级的映射类型中可以使用as子句对键做重映射如[P in keyof T as NewKey]从而筛选、改名或联合模板字面量生成新键。但仓库本主题章节并未涉及该语法此处仅作为方向提示修饰符解决的是「属性的状态」as重映射解决的是「键的身份」两者叠加即可写出Pick、Omit、Record这类更复杂的标准工具类型它们的语义说明同样收录于 类型操作章节。八、小结语法位置修饰符写在映射类型遍历语法[P in keyof T]之前。readonly/readonly为所有属性添加只读约束-readonly反向移除。?将属性标记为可选-?将可选移除、恢复必填。组合自由readonly与?可任意搭配配合/-实现四种象限。与标准库同源ReadonlyT、PartialT、RequiredT的底层就是这些修饰符手写与内置在结构类型系统下等价。边界清晰readonly是浅层、编译期约束不提供运行时不可变性。掌握了这组修饰符你不仅能读懂标准库工具类型的实现还能按需定制出自己的类型变换工具——这正是 The Concise TypeScript Book 将「映射类型」与「映射类型修饰符」设为相邻章节第 38、39 节的用意所在。想进一步巩固可回看仓库的 映射类型章节、只读属性章节 与 可选属性章节并用 TypeScript Playground 或本地tsc逐一验证上文所有示例。赞分享文档教程【免费下载链接】typescript-bookThe 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》精讲TypeScript 映射类型修饰符Mapped Type Modifiers完全指南《The Concise TypeScript Book》精讲TypeScript 映射类型修饰符Mapped Type Modifiers完全指南 映射文档教程kkFileView 如何在 GitHub Actions 中配置 PR 与 Nightly 自动运行预览 E2E 回归kkFileView 如何在 GitHub Actions 中配置 PR 与 Nightly 自动运行预览 E2E 回归 kkFileView 仓库已经内置了两文档教程Windmill App Builder 组件扩展指南从零添加一个前端 App 组件Windmill App Builder 组件扩展指南从零添加一个前端 App 组件 导读 Windmill 是一个开源开发者平台其内置的 App Buil文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表