首页
/ Expo SDK 的 TSDoc 文档规范:为 Expo API 编写可自动生成的文档注释

Expo SDK 的 TSDoc 文档规范:为 Expo API 编写可自动生成的文档注释

2026-09-05 13:43:34作者:范垣楠Rhoda

本文基于 Expo 仓库中的技能文档 .claude/skills/expo-api-docs/SKILL.md 展开,系统讲解 Expo SDK 各 expo-* 包中 TypeScript API 的 TSDoc 注释规范:第三人称陈述式写法、@platform/@example/@deprecated/@default 等标签约定、代码示例与引用块格式,以及类型导出模式。读完本文后,你可以为任何 expo-* 模块写出能被文档生成系统(GenerateDocsAPIData + TypeDoc)正确提取并渲染成 API 参考页的注释,并理解底层提取管线的实际工作方式。

何时编写 TSDoc 注释

规范明确的第一原则是:写代码的同时写文档,而不是事后补("Document APIs as you write them, not as an afterthought")。以下场景必须使用这套约定:

  • 实现会暴露公共 TypeScript API 的新功能;
  • packages/expo-* 中添加或修改公共 API;
  • 为函数、类型、接口、常量或枚举补充文档;
  • 添加平台特定注解(@platform);
  • 在 docblock 中编写代码示例。

三条核心原则

  1. 第三人称陈述式(Third-person declarative) —— 描述函数"做什么",而不是命令式地写"去做什么"。例如用 Gets...Returns...Checks...,而不是 Get...Return...
  2. 解释冰山(Explain the iceberg) —— 不仅要写参数和返回值,还要文档化失败模式、副作用、并发行为等"水面以下"的部分。
  3. 质量优于数量(Quality over quantity) —— 没有文档好过无用的文档。给一个 width 属性写 "The width" 这种描述属于典型的"无用文档",规范明确将其列为反面案例。

函数文档:标准结构

一个符合规范的函数 docblock 的完整结构如下(此示例即规范文档给出的标准范例):

/**
 * Gets the uptime since the last reboot of the device, in milliseconds.
 * Android devices do not count time spent in deep sleep.
 *
 * @return A promise fulfilled with the milliseconds since last reboot.
 *
 * @example
 * ```ts
 * const uptime = await Device.getUptimeAsync();
 * // 4371054
 * ```
 *
 * @platform android
 * @platform ios
 */
export async function getUptimeAsync(): Promise<number> {

关键要点:

  • 第一句话:说明函数做什么;
  • 后续句子:重要的行为、边界情况、平台差异;
  • 单短语描述省略句尾句号;
  • 多句描述使用句号。

在 Expo 仓库中可以找到与该范例完全对应的真实实现:packages/expo-device/src/Device.ts#L246-L262 中的 getUptimeAsync,其注释同样遵循"第一句功能描述 + 平台差异说明(Android 不计深度睡眠)+ @return + @example + 双 @platform 标签"的结构。

同文件中的 isRootedExperimentalAsync 则示范了"解释冰山"原则:注释中详细说明了为何 root/jailbreak 检测"不完全可靠"——Android 上通过查找 su 可执行文件路径实现、可能被反向工程绕过,iOS 上受 xCon 等闭源钩子方案影响,web 上恒返回 false。这就是规范要求的"文档化失败模式"。

参数文档与引用块说明

@param 的格式为 @param paramName 描述(首字母大写),参数描述中允许使用 Markdown(链接、强调、列表)、引用块(blockquote)和文档页链接:

/**
 * Sets the sensor update interval.
 *
 * @param intervalMs Desired interval in milliseconds between sensor updates.
 * > Starting from Android 12 (API level 31), the system has a 200Hz limit for each sensor updates.
 * >
 * > If you need an update interval less than 5ms, add `android.permission.HIGH_SAMPLING_RATE_SENSORS`
 * > to **app.json** `permissions` field.
 */
setUpdateInterval(intervalMs: number): void {

这段示例展示了三个典型技巧:在 @param 内用多行 > 引用块补充平台限制(Android 12 起有 200Hz 采样上限)、说明例外情况的处理方式(申请 HIGH_SAMPLING_RATE_SENSORS 权限)、以及用 Markdown 链接指向官方配置文档页。

类型与接口文档:逐属性文档化

每个属性都要单独写注释,并"教会读者一些有用的东西"。反例是 "The width",正例是 "The width of the captured photo, measured in pixels":

export type GetImageOptions = {
  /**
   * The format of the clipboard image to be converted to.
   */
  format: 'png' | 'jpeg';
  /**
   * Specify the quality of the returned image, between `0` and `1`.
   * Applicable only when `format` is set to `jpeg`, ignored otherwise.
   * @default 1
   */
  jpegQuality?: number;
};

注意 @default 标签的用法:它不解析 Markdown,在渲染时被显示为行内代码(inline code),适合直接写 @default 1@default 'png' 这类字面量。

支持的 TSDoc 标签速查表

以下是规范定义的完整标签列表,文档生成系统(TypeDoc)只会识别这些约定标签:

标签 用途 示例
@param 参数描述 @param options Configuration for the request
@return / @returns 返回值描述 @return A promise fulfilled with the result
@default 默认值(无 Markdown,渲染为行内代码) @default 1
@platform 平台可用性(android, ios, web, expo) @platform ios 11+
@example 代码示例(置于描述最底部) 见下文示例章节
@deprecated 弃用提示(自动格式化为警告样式) @deprecated Use newMethod() instead
@experimental 实验性 API 标签 @experimental
@hidden / @internal / @private 从生成的文档中隐藏 @hidden
@header 将方法归组到自定义标题下 @header Scheduling
@needsAudit 标记待安全/API 审计(注释形式,非标签) // @needsAudit
@hideType 隐藏常量上自动生成的 Type 提示块 @hideType

@platform 标签的四条规则:

  1. 全平台支持时不要使用 @platform —— 只在需要限制可用性时才添加;
  2. 多平台支持时写多个 @platform 标签(一行一个);
  3. 可以指定最低版本:@platform ios 11+
  4. 可用平台值为 androidioswebexpo(指 Expo Go 应用)。

从源码结构看,这个约定不是口头规范:文档生成器 tools/src/commands/GenerateDocsAPIData.ts#L345-L355 在配置 TypeDoc 时显式注册了扩展 blockTags 列表,包括 @deprecated@header@hideType@needsAudit@platform 等,TypeDoc 才会把这些标签解析为结构化数据交给下游渲染。

代码示例:docblock 中的标准写法

示例必须用带语言标签的三反引号围栏包裹,整体放在 @example 标签下:

/**
 * Checks device root/jailbreak status.
 *
 * @example
 * ```ts
 * const isRooted = await Device.isRootedExperimentalAsync();
 * if (isRooted) {
 *   console.warn('Device may be compromised');
 * }
 * ```
 */

规范文档中所有 @example 都展示了同一模式:调用一行 + 注释形式给出预期输出(如 // 4371054)。这种"可运行代码 + 预期输出注释"的组合让读者既能复制运行,也能直接核对结果。

引用块:Note 与 warning

重要提示使用 > blockquote 表达,且两种格式有细微区别(注意 warning 是小写):

/**
 * > **Note:** This method requires the `CAMERA` permission.
 *
 * > **warning** This method is experimental and not completely reliable.
 */
  • > **Note:** —— 信息性说明(大写 "Note" 带冒号);
  • > **warning** —— 警示(小写 "warning");
  • 多行说明每行都要以 > 开头,段落之间用空 > 分隔。

仓库实例:packages/expo-device/src/Device.ts#L286isRootedExperimentalAsync 注释首行正是 > **warning** This method is experimental and is not completely reliable.,与规范逐字吻合。

常量与枚举文档

常量:直接描述取值含义与平台差异,如 isDevice 常量(packages/expo-device/src/Device.ts#L8-L12):

/**
 * `true` if the app is running on a real device and `false` if running
 * in a simulator or emulator. On web, this is always set to `true`.
 */
export const isDevice: boolean = ExpoDevice.isDevice;

枚举:枚举本身和个别枚举值都要文档化,个别值可挂 @platform

/**
 * Type used to define what type of data is stored in the clipboard.
 */
export enum ContentType {
  PLAIN_TEXT = 'plain-text',
  HTML = 'html',
  IMAGE = 'image',
  /**
   * @platform iOS
   */
  URL = 'url',
}

这类"枚举 + 逐值注释 + 值级 @platform"的模式在 packages/expo-clipboard/src/Clipboard.types.ts 等真实类型文件中可以见到(该文件同时大量使用 @default@platform 标签,是规范落地的典型样本)。

返回值措辞:resolves to

@returns 标签中统一使用 "resolves to",遵循 MDN 的惯例:

  • 首选@returns A promise that resolves to a CameraPhoto object.
  • 也可接受@returns A promise fulfilled with a CameraPhoto object.

在正文行文中,"resolves with" 同样可以接受(例如 "The promise resolves with the parsed result")。

类型导出模式:文档生成的硬性前提

关键(Critical)规则:类型必须从入口文件(entry point)导出,否则文档生成系统根本抓取不到。 有两种标准做法:

方式一:从类型文件直接 re-export

// index.ts or MainModule.ts
export {
  type FileCreateOptions,
  type DirectoryCreateOptions,
  type FileHandle,
} from './Module.types';

方式二:import 后再导出

// Haptics.ts
import { NotificationFeedbackType, ImpactFeedbackStyle } from './Haptics.types';

// ... function implementations ...

export { NotificationFeedbackType, ImpactFeedbackStyle };

文档生成管线如何工作(源码级补充)

这条规则之所以是"硬性前提",可以从生成脚本 tools/src/commands/GenerateDocsAPIData.ts 的实现得到印证:

  1. 包到入口的映射表:脚本内部维护 PACKAGES_MAPPING(含 expo-ui 各组件的独立入口映射,如 L25-L120uiPackagesMapping),命令 generate-docs-api-data(别名 gdad,支持 -p, --packageName-s, --sdk 参数)按该映射决定每个包的 TypeDoc entryPoints。TypeDoc 只从入口提取公共导出符号——所以类型若不在入口导出,就永远不会出现在生成的 API 数据里;
  2. TypeDoc 选项:脚本以 commentStyle: 'block'jsDocCompatibility: false 启动 TypeDoc(L340-L356),即只认 /** ... */ 块注释,且必须使用上文列举的规范标签;
  3. plugin 目录合并:若包内存在 plugin/src/index.ts 及对应 tsconfig.json,脚本会二次运行 TypeDoc 并把结果(标记 _source: 'plugin')并入同一份 API 数据(L379-L404);
  4. 内联文档注入:生成 JSON 后调用 applyDocsInline 处理源码中的内联文档标记(DOCS_INLINE_TAG)(L406-L411);
  5. 输出清洗:最终 JSON 会递归剔除 idgroupskindStringoriginalNamefilessourceFileName 等内部字段并压缩输出(MINIFY_JSON = trueL424-L434)。

该命令的行为契约还有对应的测试文件 tools/src/commands/GenerateDocsAPIData.test.ts 可供查阅,用于理解映射表的维护方式。

MDX 文档页中的用法示例写法

规范同时覆盖了官方文档站(.mdx 页面)中示例代码的写法,这些写法与 docblock 规范配套,共同支撑最终的 API 参考页:

1. 代码块格式 —— 必须带语言标签,展示"代码放在哪里"时加文件路径标签:

```ts app/(tabs)/index.tsx
import * as FileSystem from 'expo-file-system';

const content = await FileSystem.readAsStringAsync(uri);
```

2. 可交互 Snack 示例

<SnackInline label="Basic file read" dependencies={['expo-file-system']}>
```tsx
import * as FileSystem from 'expo-file-system';

export default function App() {
  // ...
}
```

3. 可折叠示例

<Collapsible summary="Advanced usage with error handling">
```ts
try {
  const result = await someAsyncOperation();
} catch (error) {
  console.error('Operation failed:', error);
}
```

4. API 参考区块 —— 文档页末尾放置该组件,API 参考内容即从 TSDoc 注释自动生成:

<APISection packageName="expo-file-system" apiName="FileSystem" />

快速核对清单

要做(Do):

  • 使用第三人称陈述式("Gets"、"Returns"、"Checks");
  • 文档化参数/返回值之外的行为(失败模式、副作用、并发);
  • 平台特定 API 使用 @platform 标签;
  • 包含实用的 @example 代码块;
  • 类型从入口文件导出。

不要做(Don't):

  • 写无用描述(给 width 属性写 "The width");
  • 使用命令式语气("Get the value");
  • 对复杂行为跳过文档;
  • 忘记 re-export 类型导致文档生成抓不到;
  • 使用 @link 标签(不受支持——应使用标准 Markdown 链接);
  • 在全平台支持时添加 @platform 标签。

小结

这套规范的价值在于"注释即数据":TSDoc 注释不仅给人看,更是 tools/src/commands/GenerateDocsAPIData.ts 驱动 TypeDoc 提取结构化 API 数据的唯一来源。遵循第三人称陈述式、完整标签集(尤其是 @platform@default)、docblock 代码示例格式,以及"类型必从入口导出"这一硬性前提,新写出的 expo-* API 就能零返工地进入官方 API 参考文档体系。完整规范原文见 .claude/skills/expo-api-docs/SKILL.md,真实注释范例可参照 packages/expo-device/src/Device.tspackages/expo-clipboard/src/Clipboard.types.ts

登录后查看全文
热门项目推荐
相关项目推荐