- 开发工具
- 文档
【免费下载链接】typedoc
Documentation generator for TypeScript projects.
导读
@readonly是 TypeDoc 提供的一组修饰符标签(Modifier Tag)之一,它允许你在 TypeScript 类型系统认为某个成员“可写”的情况下,仍指示 TypeDoc 在生成文档时将其呈现为只读(non-writable)。本文结合 TypeDoc 仓库源码,完整讲解@readonly的语义、底层处理流程、渲染效果与测试用例,帮助你在 API 文档中精确表达“消费方不应修改”的设计意图。
@readonly标签语义
根据 site/tags/readonly.md 的官方说明:
The
@readonlytag indicates that a reflection should be documented as non-writable, even if writable according to TypeScript.
即:@readonly的作用是覆盖 TypeScript 本身的可写性判断。无论 TypeScript 认为该成员是否有 setter、是否可赋值,只要注释中带有@readonly,TypeDoc 就会将其记录为只读并如此渲染。
该标签属于修饰符标签(Modifier Tag),与@private、@protected、@public、@abstract、@sealed等同属一类,完整清单见 tags.md。
官方示例:getter 与 setter 的处理
原文档给出的示例展示了一个经典场景——某个属性同时定义了 getter 与 setter,但从文档视角应视为只读:
export class Readable { /** @readonly */ get prop() { return 1; } /** Will be removed from the documentation due to the readonly tag */ set prop(_: number) { throw new Error("Not permitted"); } }在这个例子中:
- getter
prop上的@readonly使整个属性在文档中被标记为只读; - setter
prop的注释也说明它会因 readonly 标签而从文档中移除。
源码级解析:@readonly的完整处理链路
1. 修饰符识别与标志设置
@readonly的解析发生在转换器插件 CommentPlugin.ts 的applyModifiers中。当注释包含@readonly修饰符时(第 234–240 行):
if (comment.hasModifier("@readonly")) { const target = reflection.kindOf(ReflectionKind.GetSignature) ? reflection.parent! : reflection; target.setFlag(ReflectionFlag.Readonly); comment.removeModifier("@readonly"); }关键逻辑在于:
- 如果反射对象是GetSignature(getter 签名),则把
Readonly标志设置到它的父级(即属性/访问器本身); - 否则直接设置到当前反射对象;
- 处理完成后会从注释中移除
@readonly修饰符,确保它不会以原始标签形式出现在渲染结果中。
ReflectionFlag.Readonly定义在 Reflection.ts 中,是一个位标志:
export enum ReflectionFlag { None = 0, // ... Readonly = 1 << 9, // ... }同时它被列入relevantFlags(第 45–53 行),并对外暴露isReadonlygetter(第 124–125 行),供渲染模板查询:
get isReadonly() { return this.hasFlag(ReflectionFlag.Readonly); }2. 解决阶段:隐藏 setter 并清理标志
在onBeginResolve(第 361–368 行)中,TypeDoc 会遍历项目中的反射,对**访问器(Accessor)**做特殊处理:
if (ref.kindOf(ReflectionKind.Accessor) && ref.flags.isReadonly) { const decl = ref as DeclarationReflection; if (decl.setSignature) { hidden.add(decl.setSignature); } // Clear flag set by @readonly since it shouldn't be rendered. ref.setFlag(ReflectionFlag.Readonly, false); }这段代码揭示了两点实现细节:
- setter 被加入隐藏集合:凡是被
@readonly标记的访问器,其setSignature会被隐藏,最终通过project.removeReflection从文档中移除——这正是原文档示例中 setter “被移除”的底层原因; - 清除访问器本身的 Readonly 标志:注释明确指出该标志“不应被渲染”(shouldn't be rendered),因为只读性最终体现在签名渲染的关键字上,而不是访问器本身上。
3. 渲染阶段:readonly关键字的输出
只读标志最终会以 TypeScript 的readonly关键字形式出现在生成的文档签名中。在默认主题的索引签名渲染中可以看到:
- templates/reflection.tsx(第 79–84 行):
{index.flags.isReadonly && ( <> <span class="tsd-signature-keyword">readonly</span> {" "} </> )}- partials/typeDetails.tsx(第 388–393 行)中也有完全相同的渲染逻辑,用于参数索引签名。
也就是说:isReadonly标志一旦置位,文档签名前就会出现readonly关键字,让读者一眼看出该成员不可写。
测试用例验证
仓库在 readonlyTag.ts 中提供了覆盖@readonly行为的测试样例,包含两种典型用法:
export class Book { /** * Technically property has a setter, but for documentation purposes it should * be presented as readonly. * @readonly */ get title(): string { return "hah"; } set title(_value: string) { throw new Error("This property is read-only!"); } /** * Should be documented as readonly because no consumer should change it. * @readonly */ author!: string; }该测试用例与原文档示例相互印证,覆盖了两个典型场景:
- 含 setter 的属性:
title在类型层面可写(存在 setter),但通过@readonly声明为文档只读; - 类属性字段:
author使用!断言(definite assignment assertion),本身是可赋值的,同样通过@readonly在文档中呈现为只读。
使用建议与注意事项
适用场景
- API 设计中的“防御性只读”:属性虽然出于实现原因保留了 setter,但设计上禁止外部修改(如内部状态、缓存值)。此时用
@readonly向文档读者明确传达契约; - 避免误导的类型系统表达:当 TypeScript 的类型信息无法表达“不可变”语义(例如定义了 setter 但会抛错、或使用
!断言声明的字段),@readonly是补充文档语义的正确工具; - 索引签名:对于索引签名(index signature),Readonly 标志同样会被渲染为
readonly关键字,可配合使用。
注意事项
@readonly只影响 TypeDoc 的文档输出,不会改变 TypeScript 的类型检查行为,不要用它替代readonly修饰符或Readonly<T>类型工具;- 标记了
@readonly的访问器的setter 会从文档中完全移除,这是预期行为而非 bug(见 CommentPlugin.ts 的隐藏逻辑); - 与
@private、@sealed等一样,它属于修饰符标签,会在转换阶段被消费并从注释中移除,不会残留在渲染文本中。
小结
@readonly是 TypeDoc 修饰符标签家族中一个简洁但实用的工具:通过一行注释即可覆盖 TypeScript 的可写性判断,将成员在文档中呈现为只读,并自动隐藏对应的 setter。其完整链路——从 CommentPlugin.ts 的标志设置、解决阶段的 setter 隐藏,到默认主题模板中的readonly关键字渲染——都体现了 TypeDoc “以注释驱动、以类型为基础”的文档生成理念。当你的 API 存在“类型可写但契约只读”的成员时,@readonly就是表达该契约的标准方式。
- 开发工具
- 文档
【免费下载链接】typedoc
Documentation generator for TypeScript projects.
相关推荐
TypeDoc `@abstract` 标签:在 TypeScript 中把“非抽象”方法标记为抽象并写入文档
TypeDoc @abstract 标签:在 TypeScript 中把“非抽象”方法标记为抽象并写入文档 本篇基于 TypeDoc 官方文档中 @abstra
开发工具文档TypeDoc @internal 标签详解:标记内部 API 并通过 --excludeInternal 从文档中移除
TypeDoc @internal 标签详解:标记内部 API 并通过 excludeInternal 从文档中移除 本文围绕 TypeDoc 的 @inter
开发工具文档TypeDoc @deprecated 标签详解:从文档标记到删除线渲染的完整机制
TypeDoc @deprecated 标签详解:从文档标记到删除线渲染的完整机制 本文基于 TypeDoc 官方文档 site/tags/deprecated
开发工具文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考