Skip to Content
JavaScript7 JSDoc

7 JSDoc

JSDoc  用于所有类、字段和方法。

7.1 一般形式

JSDoc 块的基本格式如以下示例所示:

/** * Multiple lines of JSDoc text are written here, * wrapped normally. * @param {number} arg A number to do something to. */ function doSomething(arg) { … }

或在此单行示例中:

/** @const @private {!Foo} A short bit of JSDoc. */ this.foo_ = foo;

如果单行注释溢出到多行,则必须使用多行风格,/***/ 各占一行。

许多工具从 JSDoc 注释中提取元数据以执行代码验证和优化。因此,这些注释必须格式良好。

7.2 Markdown

JSDoc 使用 Markdown 编写,但必要时可以包含 HTML。

请注意,自动提取 JSDoc 的工具(例如 JsDossier )通常会忽略纯文本格式,所以如果你这样写:

/** * Computes weight based on three factors: * items sent * items received * last timestamp */

输出会是这样的:

Computes weight based on three factors: items sent items received last timestamp

相反,请使用 Markdown 列表:

/** * Computes weight based on three factors: * * - items sent * - items received * - last timestamp */

7.3 JSDoc 标签

Google 风格允许 JSDoc 标签的一个子集。完整列表参见 ??。大多数标签必须占据自己的行,标签在行首。

不允许:

/** * The "param" tag must occupy its own line and may not be combined. * @param {number} left @param {number} right */ function add(left, right) { ... }

不需要额外数据的简单标签(如 @private@const@final@export)可以合并到同一行,并在适当时附带可选类型。

/** * Place more complex annotations (like "implements" and "template") * on their own lines. Multiple simple tags (like "export" and "final") * may be combined in one line. * @export @final * @implements {Iterable<TYPE>} * @template TYPE */ class MyClass { /** * @param {!ObjType} obj Some object. * @param {number=} num An optional number. */ constructor(obj, num = 42) { /** @private @const {!Array<!ObjType|number>} */ this.data_ = [obj, num]; } }

何时合并标签或以什么顺序合并没有硬性规则,但要保持一致。

有关在 JavaScript 中注解类型的一般信息,参见 Annotating JavaScript for the Closure Compiler Types in the Closure Type System 

7.4 换行

换行的块标签缩进四个空格。换行的描述文本可以与前一行的描述对齐,但不推荐这种水平对齐。

/** * Illustrates line wrapping for long param/return descriptions. * @param {string} foo This is a param with a description too long to fit in * one line. * @return {number} This returns something that has a description too long to * fit in one line. */ exports.method = function(foo) { return 5; };

换行 @desc@fileoverview 描述时不要缩进。

7.5 顶部/文件级注释

文件可以有顶级文件概述。版权声明、作者信息和默认可见性级别是可选的。当文件包含多个类定义时,通常建议添加文件概述。顶级注释旨在帮助不熟悉代码的读者了解此文件中的内容。如果存在,它可以提供文件内容的描述以及任何依赖项或兼容性信息。换行的行不缩进。

示例:

/** * @fileoverview Description of file, its uses and information * about its dependencies. * @package */

7.6 类注释

类、接口和 record 必须用描述和任何模板参数、实现的接口、可见性或其他适当的标签进行文档化。类描述应为读者提供足够的信息以了解如何以及何时使用该类,以及正确使用该类所需的任何其他注意事项。构造函数上的文本描述可以省略。当使用 class 关键字定义类时,不使用 @constructor@extends 注解,除非它扩展了泛型类。定义 @interface@record 时,定义子类时使用 @extends 注解,永远不使用 extends 关键字。

/** * A fancier event target that does cool things. * @implements {Iterable<string>} */ class MyFancyTarget extends EventTarget { /** * @param {string} arg1 An argument that makes this more interesting. * @param {!Array<number>} arg2 List of numbers to be processed. */ constructor(arg1, arg2) { // ... } }; /** * Records are also helpful. * @extends {Iterator<TYPE>} * @record * @template TYPE */ class Listable { /** @return {TYPE} The next item in line to be returned. */ next() {} }

7.7 枚举和 typedef 注释

所有枚举和 typedef 必须在前一行用适当的 JSDoc 标签(@typedef@enum)进行文档化。公共枚举和 typedef 还必须有描述。各个枚举项可以在前一行用 JSDoc 注释进行文档化。

/** * A useful type union, which is reused often. * @typedef {!FruitType|!FruitTypeEnum} */ let CoolUnionType; /** * Types of fruits. * @enum {string} */ const FruitTypeEnum = { /** This kind is very sour. */ SOUR: 'sour', /** The less-sour kind. */ SWEET: 'sweet', };

Typedef 对于定义简短的 record 类型、联合类型、复杂函数或泛型类型的别名很有用。对于具有许多字段的 record 类型应避免使用 typedef,因为它们不允许对各个字段进行文档化,也不允许使用模板或递归引用。对于大型 record 类型,优先使用 @record

7.8 方法和函数注释

在方法和具名函数中,参数和返回类型必须进行文档化,即使在同签名的 @override 中也是如此。必要时应记录 this 类型。如果函数没有非空的 return 语句,可以省略返回类型。

如果方法、参数和返回描述(但不是类型)从方法的其余 JSDoc 或其签名中显而易见,则可以省略。

方法描述以描述方法功能的动词短语开头。此短语不是祈使句,而是以第三人称编写,就好像在它前面有一个隐含的“此方法……”。

如果方法重写了超类方法,它必须包含 @override 注解。对于重写的方法,即使没有从超类方法细化类型,也必须显式指定所有 @param@return 注解。这是为了与 TypeScript 对齐。

/** A class that does something. */ class SomeClass extends SomeBaseClass { /** * Operates on an instance of MyClass and returns something. * @param {!MyClass} obj An object that for some reason needs detailed * explanation that spans multiple lines. * @param {!OtherClass} obviousOtherClass * @return {boolean} Whether something occurred. */ someMethod(obj, obviousOtherClass) { ... } /** * @param {string} param * @return {string} * @override */ overriddenMethod(param) { ... } } /** * Demonstrates how top-level functions follow the same rules. This one * makes an array. * @param {TYPE} arg * @return {!Array<TYPE>} * @template TYPE */ function makeArray(arg) { ... }

如果你只需要记录函数的参数和返回类型,可以选择性地在函数签名中使用内联 JSDoc。这些内联 JSDoc 指定返回和参数类型而无需标签。

function /** string */ foo(/** number */ arg) {...}

如果需要描述或标签,请在方法上方使用单个 JSDoc 注释。例如,返回值的方法需要 @return 标签。

class MyClass { /** * @param {number} arg * @return {string} */ bar(arg) {...} }
// Illegal inline JSDocs. class MyClass { /** @return {string} */ foo() {...} } /** No function description allowed inline here. */ function bar() {...} function /** Function description is also illegal here. */ baz() {...}

在匿名函数中,注解通常是可选的。如果自动类型推断不够或显式注解能提高可读性,则像这样注解参数和返回类型:

promise.then( /** @return {string} */ (/** !Array<string> */ items) => { doSomethingWith(items); return items[0]; });

关于函数类型表达式,参见 ??

7.9 属性注释

属性类型必须进行文档化。如果名称和类型提供了足够的文档来理解代码,则可以省略私有属性的描述。

公开导出的常量以与属性相同的方式注释。

/** My class. */ class MyClass { /** @param {string=} someString */ constructor(someString = 'default string') { /** @private @const {string} */ this.someString_ = someString; /** @private @const {!OtherType} */ this.someOtherThing_ = functionThatReturnsAThing(); /** * Maximum number of things per pane. * @type {number} */ this.someProperty = 4; } } /** * The number of times we'll try before giving up. * @const {number} */ MyClass.RETRY_COUNT = 33;

7.10 类型注解

类型注解出现在 @param@return@this@type 标签上,并可选地出现在 @const@export 和任何可见性标签上。附加到 JSDoc 标签的类型注解必须始终用花括号括起来。

7.10.1 可空性(Nullability)

类型系统定义了修饰符 !?,分别用于非空和可空。这些修饰符必须位于类型之前。

可空性修饰符对不同类型有不同的要求,分为两大类:

  1. 原始类型(stringnumberbooleansymbolundefinednull)和字面量({function(...): ...}{{foo: string...}})的类型注解默认始终是非空的。使用 ? 修饰符使其可空,但省略冗余的 !
  2. 引用类型(通常是 UpperCamelCase 的任何内容,包括 some.namespace.ReferenceType)引用在其他地方定义的类、枚举、recordtypedef。由于这些类型可能是也可能不是可空的,因此仅从名称无法判断它是否可空。始终对这些类型使用显式的 ?! 修饰符,以防止在使用位置产生歧义。

不好的:

const /** MyObject */ myObject = null; // Non-primitive types must be annotated. const /** !number */ someNum = 5; // Primitives are non-nullable by default. const /** number? */ someNullableNum = null; // ? should precede the type. const /** !{foo: string, bar: number} */ record = ...; // Already non-nullable. const /** MyTypeDef */ def = ...; // Not sure if MyTypeDef is nullable. // Not sure if object (nullable), enum (non-nullable, unless otherwise // specified), or typedef (depends on definition). const /** SomeCamelCaseName */ n = ...;

好的:

const /** ?MyObject */ myObject = null; const /** number */ someNum = 5; const /** ?number */ someNullableNum = null; const /** {foo: string, bar: number} */ record = ...; const /** !MyTypeDef */ def = ...; const /** ?SomeCamelCaseName */ n = ...;

7.10.2 类型转换(Type Casts)

在编译器无法准确推断表达式类型且 goog.asserts  中的断言函数无法解决的情况下,可以通过添加类型注解注释并将表达式括在括号中来收紧类型。请注意,括号是必需的。

/** @type {number} */ (x)

7.10.3 模板参数类型

始终指定模板参数。这样编译器可以做得更好,也使读者更容易理解代码的功能。

不好的:

const /** !Object */ users = {}; const /** !Array */ books = []; const /** !Promise */ response = ...;

好的:

const /** !Object<string, !User> */ users = {}; const /** !Array<string> */ books = []; const /** !Promise<!Response> */ response = ...; const /** !Promise<undefined> */ thisPromiseReturnsNothingButParameterIsStillUseful = ...; const /** !Object<string, *> */ mapOfEverything = {};

不应使用模板参数的情况:

  • Object 用于类型层级结构而非类似 map 的结构。

7.10.4 函数类型表达式

术语说明:*函数类型表达式(function type expression)*指注解中使用 function 关键字的函数类型注解(参见下面的示例)。

在给出函数定义的地方,不要使用函数类型表达式。使用 @param@return 或内联注解来指定参数和返回类型(参见 ??)。这包括匿名函数和定义并赋值给 const 的函数(其中函数 JSDoc 出现在整个赋值表达式上方)。

函数类型表达式是必要的,例如在 @typedef@param@return 中。对于函数类型的变量或属性,如果它们没有立即用函数定义初始化,也使用它。

/** @private {function(string): string} */ this.idGenerator_ = googFunctions.identity;

使用函数类型表达式时,始终显式指定返回类型。否则默认返回类型是“unknown”(?),这会导致奇怪和意外的行为,而且很少是实际期望的。

不好的 - 类型错误,但不给出警告:

/** @param {function()} generateNumber */ function foo(generateNumber) { const /** number */ x = generateNumber(); // No compile-time type error here. } foo(() => 'clearly not a number');

好的:

/** * @param {function(): *} inputFunction1 Can return any type. * @param {function(): undefined} inputFunction2 Definitely doesn't return * anything. * NOTE: the return type of `foo` itself is safely implied to be {undefined}. */ function foo(inputFunction1, inputFunction2) {...}

7.10.5 空白

在类型注解中,每个逗号或冒号后面需要一个空格或换行。可以插入额外的换行以提高可读性或避免超过列限制。这些换行应根据适用的指南(例如 ????)进行选择和缩进。类型注解中不允许使用其他空白。

好的:

/** @type {function(string): number} */ /** @type {{foo: number, bar: number}} */ /** @type {number|string} */ /** @type {!Object<string, string>} */ /** @type {function(this: Object<string, string>, number): string} */ /** * @type {function( * !SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak, * !OtherVeryLongTypedef): string} */ /** * @type {!SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak| * !OtherVeryLongTypedef} */

不好的:

// Only put a space after the colon /** @type {function(string) : number} */ // Put spaces after colons and commas /** @type {{foo:number,bar:number}} */ // No space in union types /** @type {number | string} */

7.11 可见性注解

可见性注解(@private@package@protected)可以在 @fileoverview 块中或任何导出的符号或属性上指定。不要为局部变量指定可见性,无论是在函数内还是在模块顶层。@private 名称可以选择性地以下划线结尾。

Last updated on