ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

TypeScript readonly 只读属性全解析:编译期写入保护、Readonly\<T\> 与浅层不可变的边界(The Concise TypeScript Book 实战解读)

2026/9/28 20:55:30 拓冰建站 浏览量
TypeScript readonly 只读属性全解析:编译期写入保护、Readonly\<T\> 与浅层不可变的边界(The Concise TypeScript Book 实战解读) 文档教程【免费下载链接】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点击查看免费下载readonly是 TypeScript 中最常用的写入守卫之一它能在编译期阻止属性被重新赋值却并不承诺对象内部数据的深度不可变。本文以开源仓库 The Concise TypeScript Book 中 readonly-properties.md意大利语版 为核心骨架系统讲解readonly的四种声明姿势、与const的区别、映射类型修饰符readonly/-readonly、ReadonlyT工具类型以及as const的配合用法并结合仓库源码章节给出可直接运行验证的完整示例。读完你将掌握在接口、类型别名、类、索引签名与泛型工具类型中正确使用readonly的实战方案并明确只读 ≠ 不可变这一关键边界。一、readonly 是什么一次编译期的写入守卫TypeScript 通过readonly修饰符阻止对属性的再次写入re-write。用原文档的原话来说使用readonly修饰符可以阻止对属性的写入它确保属性不能被重写但不提供任何完全不可变的保证。理解这句话需要抓住两个层面它是编译期约束readonly只存在于类型层面TypeScript 编译器在类型检查阶段拒绝赋值但编译产出的 JavaScript 中不会留下任何痕迹运行时对象属性依然是可写的。它是浅层约束readonly只限制直接给该属性赋值这一操作不限制该属性所引用对象内部的可变性。因此readonly最适合用来表达接口契约层面的不可写而非运行时深度冻结后者应使用Object.freeze()或 Immutable.js 类库。二、声明只读属性的四种方式原文档核心内容原文档给出了四种等价或相近的声明方式这里完整保留并逐一展开// 方式一在 interface 中使用 readonly 修饰符 interface Y { readonly a: number; } // 方式二在 type 别名中使用 readonly 修饰符 type X { readonly a: number; }; // 方式三使用内置工具类型 ReadonlyT一次性把所有属性变为只读 type J Readonly{ a: number; }; // 方式四只读索引签名readonly index signature type K { readonly [index: number]: string; };方式一与方式二显式标注单个属性。当类型中只有个别属性需要防写时直接在属性名后加readonly即可语义最清晰。方式三ReadonlyT批量只读化。当你希望整体只读而不想逐个属性加关键字时使用内置工具类型ReadonlyT它会把 T 的所有属性都标记为只读。仓库在 type-manipulation.md 的 Utility Types 一节给出了带运行验证的完整示例type Person { name: string; age: number; }; type A ReadonlyPerson; const a: A { name: Simon, age: 17 }; a.name John; // 编译错误Cannot assign to name because it is a read-only property注意ReadonlyT的只读与显式readonly的只读在类型层面是完全一致的——ReadonlyY的成员与手工readonly声明的成员互相兼容。它同样只做浅层处理嵌套对象的内部属性不会被递归只读化Readonly{ nested: { x: number } }中的nested.x依然可写。方式四只读索引签名。readonly [index: number]: string表示任何数字下标对应的元素都不可重新赋值即这个数字索引集合整体只读。这对于映射表、字典、数组样式的只读集合非常实用type K { readonly [index: number]: string; }; declare const map: K; map[0] x; // 编译错误Index signature in type K only permits reading在仓库英文原版文档 readonly-properties.md 中这四种写法被并列展示说明它们是同一语义下的不同表达手段可依据是针对单个属性、整个类型还是整个索引集合来选择。三、readonly 与 const一对容易被混淆的孪生兄弟readonly常被拿来与const比较二者都不能再次赋值但作用层级完全不同维度constreadonly作用对象变量绑定/引用本身对象的属性生效时机编译期 运行时引用不可再绑定仅编译期类型检查粒度整个变量绑定可精确到类型中的单个属性典型场景const x [1, 2, 3]interface Y { readonly a: number }一个典型的记忆点是const声明一个对象后对象属性依然可写const obj.a 1是合法的而readonly恰恰是管属性、不管变量绑定的补位角色。二者配合使用才能同时锁住引用与属性两个维度。四、为什么 readonly 不等于不可变边界与限制4.1 浅层性Shallowreadonly只禁止给该属性赋值不禁止修改该属性指向对象的内部状态interface Config { readonly settings: { retries: number }; } const config: Config { settings: { retries: 3 } }; config.settings { retries: 5 }; // 编译错误只读属性不可赋值 config.settings.retries 5; // 合法嵌套对象的内部属性仍然可变要实现深层只读需要递归的映射类型如DeepReadonlyT这属于本书 mapped-types.md 与 mapped-type-modifiers.md 探讨的通过keyof映射既有类型的能力范畴而非readonly本身能解决的问题。4.2 运行时无保护readonly在编译后被完全擦除。以下代码能通过编译但运行时obj.a 2会真实生效interface Y { readonly a: number; } const obj: Y { a: 1 }; // 编译期拦截 obj.a 2; // 编译错误 // 若绕过类型检查如通过 any 或 JS 调用方运行时赋值不会被阻止这印证了原文档的核心论断readonly提供的是契约级保证不是运行时保证。当需要运行时防篡改时应叠加Object.freeze()。五、映射类型修饰符readonly 与 -readonly批量生产与撤销只读readonly不仅能手写还能作为映射类型的修饰符参与类型变换。仓库 mapped-type-modifiers.md意大利语版 明确指出三种修饰符readonly或readonly把映射类型中的属性标记为只读-readonly把映射类型中的属性从只读改回可变?把属性标记为可选。原文档示例type ReadOnlyT { readonly [P in keyof T]: T[P] }; // 所有属性标记为只读 type MutableT { -readonly [P in keyof T]: T[P] }; // 所有属性标记为可变 type MyPartialT { [P in keyof T]?: T[P] }; // 所有属性标记为可选readonly/-readonly的加减法语义是本节关键。-readonly可以逆转既有的只读类型——例如从ReadonlyPerson还原出可变版本type Person { name: string; age: number }; type Frozen ReadonlyPerson; type Thawed MutableFrozen; // { name: string; age: number }可写而ReadonlyT这个内置工具类型的标准实现正是映射类型修饰符的应用type ReadonlyT { readonly [P in keyof T]: T[P] };因此predefined-conditional-types.md意大利语版 中把ReadonlyType与PartialType、RequiredType并列介绍是有深意的三者分别通过映射修饰符readonly、?的正反方向把不可变 / 可选 / 必选这三种属性特征批量施加到类型上构成了一套完整的属性特征开关工具箱。六、只读数组与 as const让只读从属性扩展到集合readonly修饰符同样适用于数组与元组类型// 只读数组可用 ReadonlyArrayT 或 readonly T[] 表示 const list: readonly number[] [1, 2, 3]; list.push(4); // 编译错误Property push does not exist on type readonly number[] list[0] 99; // 编译错误 // 只读元组 const tuple: readonly [number, string] [1, a]; tuple[0] 2; // 编译错误仓库 exploring-the-type-system.md 的 Const assertion 一节进一步展示了as const如何在值层面触发只读推断const x [1, 2, 3]; // 推断类型number[]可变数组 const y [1, 2, 3] as const; // 推断类型readonly [1, 2, 3]只读元组as const会把字面量断言为最窄的只读形式它其实等价于把每个成员当作字面量类型 把整个值标记为 readonly常用于固定配置项、枚举式字典和需要精确字面量类型的场景。原文档强调的readonly 不提供完全不可变的保证在as const上同样成立它保护的是这个值本身不被改写而不是内部引用的深层对象。七、readonly 的实战定位API 契约、配置对象与不可变数据流综合原文档与仓库各章节readonly最典型的应用场景有三类1. 数据契约DTO / API 响应对外部传入、不希望被调用方改写的对象使用readonly把只读写进类型契约让协作者在编译期就拿到正确约束interface ApiResponse { readonly id: string; readonly status: ok | error; readonly data: Recordstring, unknown; }2. 配置对象模块级配置加载后不应再被改动用ReadonlyConfig或逐属性readonly表达只读配置type Config { host: string; port: number; debug: boolean }; const CONFIG: ReadonlyConfig { host: localhost, port: 8080, debug: false }; // CONFIG.port 9090; // 编译错误3. 函数参数防改写接收方若不想承担参数被意外修改的副作用可声明readonly参数类型对象属性级function render(user: Readonly{ name: string; age: number }) { // user.name x; // 编译错误只读属性不可赋值 return ${user.name} (${user.age}); }需要注意的配套实践是一旦某类型被打上readonly通过它修改数据的代码就会被编译期拦截因此更合理的模式是只读类型负责读取与传递可变类型负责构建与更新两者用映射类型显式转换参考第五节-readonly这与本书 differences-between-type-and-interface.md、type-manipulation.md 中组合、变换既有类型的整体思想一脉相承。八、小结一张图记住 readonly做什么编译期阻止属性重新赋值四种声明方式接口readonly、类型别名readonly、ReadonlyT、只读索引签名语义等价不做什么不保证深度不可变浅层约束、不提供运行时保护编译期擦除、不管const管的变量绑定进阶配合readonly/-readonly映射修饰符批量施加或撤销只读readonly数组/元组与as const把只读扩展到集合类型ReadonlyT是PartialT、RequiredT同族的属性特征工具类型之一。想深入验证以上示例可以直接阅读仓库中的权威原文英文版 readonly-properties.md、意大利语版本文主体、映射类型修饰符章节 mapped-type-modifiers.md、内置工具类型章节 type-manipulation.md 以及类型系统总览 exploring-the-type-system.md把文中每个示例粘贴到 TypeScript Playground 或项目里编译验证即可。赞分享文档教程【免费下载链接】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 精读readonly 只读属性——编译期防写保护与不变性边界The Concise TypeScript Book 精读readonly 只读属性——编译期防写保护与不变性边界 readonly 是 TypeScrip文档教程The Concise TypeScript Book 精读readonly 只读属性——编译期写入保护与类型安全实践The Concise TypeScript Book 精读readonly 只读属性——编译期写入保护与类型安全实践 导读 readonly 是 TypeS文档教程The Concise TypeScript Book 精读只读属性readonly深入解析与实战The Concise TypeScript Book 精读只读属性readonly深入解析与实战 readonly 是 TypeScript 在类型层面文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考