下面的列表概述了在 JavaScript 文件中使用 JSDoc 注解提供类型信息时,目前支持哪些构造。
注意:
- 下面未明确列出的标签(例如
@async)尚不支持。 - TypeScript 文件中只支持文档标签。其余标签仅在 JavaScript 文件中受支持。
类型
@type@import@param(或@arg或@argument)@returns(或@return)@typedef@callback@template@satisfies
类
- 属性修饰符
@public,@private,@protected,@readonly @override@extends(或@augments)@implements@class(或@constructor)@this
文档
文档标签在 TypeScript 和 JavaScript 中均可使用。
其他
它们的含义通常与 jsdoc.app 上给出的标签含义相同或是其超集。下面的代码说明了各标签的差异,并给出了一些用法示例。
注意:你可以使用Playground 来探索 JSDoc 支持。
类型
@type
你可以使用“@type”标签引用类型。类型可以是:
- 原始类型,如
string或number。 - 在 TypeScript 声明中声明的类型,可以是全局的或导入的。
- 在 JSDoc
@typedef标签中声明的类型。
你可以使用大多数 JSDoc 类型语法以及任意 TypeScript 语法,从最基础的 string到最高级的条件类型。
jsTry/*** @type {string}*/vars ;/** @type {Window} */varwin ;/** @type {PromiseLike<string>} */varpromisedString ;// You can specify an HTML Element with DOM properties/** @type {HTMLElement} */varmyElement =document .querySelector (selector );element .dataset .myData = "";
@type 可以指定联合类型——例如,某个值既可以是 string 也可以是 boolean。
jsTry/*** @type {string | boolean}*/varsb ;
你可以使用多种语法指定数组类型:
jsTry/** @type {number[]} */varns ;/** @type {Array.<number>} */varjsdoc ;/** @type {Array<number>} */varnas ;
你也可以指定对象字面量类型。例如,具有属性 ‘a’(string)和 ‘b’(number)的对象使用以下语法:
jsTry/** @type {{ a: string, b: number }} */varvar9 ;
你可以使用字符串和数字索引签名来指定类映射对象和类数组对象,既可以使用标准 JSDoc 语法,也可以使用 TypeScript 语法。
jsTry/*** A map-like object that maps arbitrary `string` properties to `number`s.** @type {Object.<string, number>}*/varstringToNumber ;/** @type {Object.<number, object>} */vararrayLike ;
上述两种类型分别等价于 TypeScript 类型 { [x: string]: number } 和 { [x: number]: any }。编译器能理解这两种语法。
你可以使用 TypeScript 或 Google Closure 语法指定函数类型:
jsTry/** @type {function(string, boolean): number} Closure syntax */varsbn ;/** @type {(s: string, b: boolean) => number} TypeScript syntax */varsbn2 ;
或者你也可以直接使用未指定类型的 Function 类型:
jsTry/** @type {Function} */varfn7 ;/** @type {function} */varfn6 ;
Closure 中的其他类型同样可用:
jsTry/*** @type {*} - can be 'any' type*/varstar ;/*** @type {?} - unknown type (same as 'any')*/varquestion ;
类型转换
TypeScript 借用了 Google Closure 的类型转换语法。你可以在任何带括号的表达式前添加 @type 标签,从而将类型转换为其他类型。
jsTry/*** @type {number | string}*/varnumberOrString =Math .random () < 0.5 ? "hello" : 100;vartypeAssertedNumber = /** @type {number} */ (numberOrString );
你甚至可以像 TypeScript 那样转换为 const:
jsTryletone = /** @type {const} */(1);
导入类型
你可以使用导入类型从其他文件导入声明。这种语法是 TypeScript 特有的,与 JSDoc 标准不同:
jsTry// @filename: types.d.tsexport typePet = {name : string,};// @filename: main.js/*** @param {import("./types").Pet} p*/functionwalk (p ) {console .log (`Walking ${p .name }...`);}
当你不知道某个值的类型,或者它的类型很大、写起来很麻烦时,可以使用导入类型从模块中获取该值的类型:
jsTry/*** @type {typeof import("./accounts").userAccount}*/varx =require ("./accounts").userAccount ;
@import
@import 标签可以让我们引用其他文件的导出。
jsTry/*** @import {Pet} from "./types"*//*** @type {Pet}*/varmyPet ;myPet .name ;
这些标签在运行时并不会真正导入文件,它们引入作用域的符号只能在 JSDoc 注释中用于类型检查。
jsTry// @filename: dog.jsexport classDog {woof () {console .log ("Woof!");}}// @filename: main.js/** @import { Dog } from "./dog.js" */constd = newDog (); // error!
@param 和 @returns
@param 使用与 @type 相同的类型语法,但增加了参数名。还可以用方括号把参数名括起来,将其声明为可选参数:
jsTry// Parameters may be declared in a variety of syntactic forms/*** @param {string} p1 - A string param.* @param {string=} p2 - An optional param (Google Closure syntax)* @param {string} [p3] - Another optional param (JSDoc syntax).* @param {string} [p4="test"] - An optional param with a default value* @returns {string} This is the result*/functionstringsStringStrings (p1 ,p2 ,p3 ,p4 ) {// TODO}
同样,函数的返回类型也可以这样写:
jsTry/*** @return {PromiseLike<string>}*/functionps () {}/*** @returns {{ a: string, b: number }} - May use '@returns' as well as '@return'*/functionab () {}
@typedef、@callback 和 @param
你可以用 @typedef 定义复杂类型。@param 也支持类似的语法。
jsTry/*** @typedef {Object} SpecialType - creates a new type named 'SpecialType'* @property {string} prop1 - a string property of SpecialType* @property {number} prop2 - a number property of SpecialType* @property {number=} prop3 - an optional number property of SpecialType* @prop {number} [prop4] - an optional number property of SpecialType* @prop {number} [prop5=42] - an optional number property of SpecialType with default*//** @type {SpecialType} */varspecialTypeObject ;specialTypeObject .prop3 ;
第一行可以使用 object 或 Object。
jsTry/*** @typedef {object} SpecialType1 - creates a new type named 'SpecialType1'* @property {string} prop1 - a string property of SpecialType1* @property {number} prop2 - a number property of SpecialType1* @property {number=} prop3 - an optional number property of SpecialType1*//** @type {SpecialType1} */varspecialTypeObject1 ;
@param 允许使用类似的语法进行一次性类型标注。注意,嵌套属性名必须以参数名作为前缀:
jsTry/*** @param {Object} options - The shape is the same as SpecialType above* @param {string} options.prop1* @param {number} options.prop2* @param {number=} options.prop3* @param {number} [options.prop4]* @param {number} [options.prop5=42]*/functionspecial (options ) {return (options .prop4 || 1001) +options .prop5 ;}
@callback 与 @typedef 类似,但它指定的是函数类型而不是对象类型:
jsTry/*** @callback Predicate* @param {string} data* @param {number} [index]* @returns {boolean}*//** @type {Predicate} */constok = (s ) => !(s .length % 2);
当然,这些类型都可以在单行 @typedef 中使用 TypeScript 语法声明:
js/** @typedef {{ prop1: string, prop2: string, prop3?: number }} SpecialType *//** @typedef {(data: string, index?: number) => boolean} Predicate */
@template
你可以用 @template 标签声明类型参数。这样就能让函数、类或类型具有泛型:
jsTry/*** @template T* @param {T} x - A generic parameter that flows through to the return type* @returns {T}*/functionid (x ) {returnx ;}consta =id ("string");constb =id (123);constc =id ({});
使用逗号或多个标签可以声明多个类型参数:
js/*** @template T,U,V* @template W,X*/
你也可以在类型参数名之前指定类型约束。只有列表中的第一个类型参数会受到约束:
jsTry/*** @template {string} K - K must be a string or string literal* @template {{ serious(): string }} Seriousalizable - must have a serious method* @param {K} key* @param {Seriousalizable} object*/functionseriousalize (key ,object ) {// ????}
最后,你还可以为类型参数指定默认值:
jsTry/** @template [T=object] */classCache {/** @param {T} initial */constructor(initial ) {}}letc = newCache ()
@satisfies
@satisfies 让你可以使用 TypeScript 中的后缀操作符 satisfies。satisfies 用于声明某个值实现了某个类型,但不会影响该值的类型。
jsTry// @ts-check/*** @typedef {"hello world" | "Hello, world"} WelcomeMessage*//** @satisfies {WelcomeMessage} */constmessage = "hello world"/** @Type '"Hello world!"' does not satisfy the expected type 'WelcomeMessage'.1360Type '"Hello world!"' does not satisfy the expected type 'WelcomeMessage'.satisfies {WelcomeMessage} */constfailingMessage = "Hello world!"/** @type {WelcomeMessage} */constmessageUsingType = "hello world"
类
类可以声明为 ES6 类。
jsTryclassC {/*** @param {number} data*/constructor(data ) {// property types can be inferredthis.name = "foo";// or set explicitly/** @type {string | null} */this.title = null;// or simply annotated, if they're set elsewhere/** @type {number} */this.size ;this.initialize (data ); // Should error, initializer expects a string}/*** @param {string} s*/initialize = function (s ) {this.size =s .length ;};}varc = newC (0);// C should only be called with new, but// because it is JavaScript, this is allowed and// considered an 'any'.varresult =C (1);
也可以声明为构造函数;为此需要使用 @constructor 和 @this。
属性修饰符
@public、@private 和 @protected 的作用与 TypeScript 中的 public、private 和 protected 完全一样:
jsTry// @ts-checkclassCar {constructor() {/** @private */this.identifier = 100;}printIdentifier () {console .log (this.identifier );}}constc = newCar ();Property 'identifier' is private and only accessible within class 'Car'.2341Property 'identifier' is private and only accessible within class 'Car'.console .log (c .); identifier
@public始终是隐含的,可以省略,表示属性可以从任何地方访问。@private表示属性只能在包含它的类中使用。@protected表示属性只能在包含它的类及其所有派生子类中使用,但不能在包含该类的不同实例上使用。
@public、@private 和 @protected 在构造函数中不起作用。
@readonly
@readonly 修饰符确保属性只在初始化时被写入。
jsTry// @ts-checkclassCar {constructor() {/** @readonly */this.identifier = 100;}printIdentifier () {console .log (this.identifier );}}constc = newCar ();console .log (c .identifier );
@override
@override 的用法与 TypeScript 中相同;将它用在重写基类方法的方法上:
jsTryexport classC {m () { }}classD extendsC {/** @override */m () { }}
在 tsconfig 中设置 noImplicitOverride: true 以检查重写。
@extends
当 JavaScript 类继承泛型基类时,没有传递类型实参的 JavaScript 语法。@extends 标签可以实现这一点:
jsTry/*** @template T* @extends {Set<T>}*/classSortableSet extendsSet {// ...}
注意,@extends 只对类有效。目前,构造函数无法继承类。
@implements
同样,JavaScript 也没有实现 TypeScript 接口的语法。@implements 标签的用法与 TypeScript 中完全一致:
jsTry/** @implements {Print} */classTextBook {// TODO}}
@constructor
编译器会根据 this 属性赋值推断构造函数,不过如果你添加 @constructor 标签,就能让检查更严格、建议更完善:
jsTry/*** @constructor* @param {number} data*/functionC (data ) {// property types can be inferredthis.name = "foo";// or set explicitly/** @type {string | null} */this.title = null;// or simply annotated, if they're set elsewhere/** @type {number} */this.size ;this.Argument of type 'number' is not assignable to parameter of type 'string'.2345Argument of type 'number' is not assignable to parameter of type 'string'.initialize (); data }/*** @param {string} s*/C .prototype .initialize = function (s ) {this.size =s .length ;};varc = newC (0);c .size ;varValue of type 'typeof C' is not callable. Did you mean to include 'new'?2348Value of type 'typeof C' is not callable. Did you mean to include 'new'?result =C (1);
使用 @constructor 后,构造函数 C 内部的 this 会接受检查,因此你会得到 initialize 方法的建议,而传入数字则会报错。如果你调用 C 而不是构造它,编辑器还可能显示警告。
遗憾的是,这意味着既可调用又能作为构造函数的函数无法使用 @constructor。
@this
当有一些上下文可供参考时,编译器通常能推断出 this 的类型。如果推断不出来,你可以用 @this 显式指定 this 的类型:
jsTry/*** @this {HTMLElement}* @param {*} e*/functioncallbackForLater (e ) {this.clientHeight =parseInt (e ); // should be fine!}
文档
@deprecated
当函数、方法或属性被弃用时,你可以用 /** @deprecated */ JSDoc 注释标记它,让用户知晓。该信息会显示在补全列表中,并作为一条建议诊断信息呈现,编辑器可以对其做特殊处理。在 VS Code 这类编辑器中,已弃用的值通常以删除线样式显示,像这样。
jsTry/** @deprecated */constapiV1 = {};constapiV2 = {};apiV ;
@see
@see 让你可以链接到程序中的其他名称:
tsTrytypeBox <T > = {t :T }/** @see Box for implementation details */typeBoxify <T > = { [K in keyofT ]:Box <T > };
有些编辑器会把 Box 变成链接,方便来回跳转。
@link
@link 与 @see 类似,区别在于它可以用于其他标签内部:
tsTrytypeBox <T > = {t :T }/** @returns A {@link Box} containing the parameter. */functionbox <U >(u :U ):Box <U > {return {t :u };}
你也可以链接某个属性:
tsTrytypePet = {name : stringhello : () => string}/*** Note: you should implement the {@link Pet.hello} method of Pet.*/functionhello (p :Pet ) {p .hello ()}
或者带一个可选名称:
tsTrytypePet = {name : stringhello : () => string}/*** Note: you should implement the {@link Pet.hello | hello} method of Pet.*/functionhello (p :Pet ) {p .hello ()}
其他
@enum
@enum 标签允许你创建一个对象字面量,其成员全部属于指定的类型。与 JavaScript 中的大多数对象字面量不同,它不允许存在其他成员。@enum 旨在与 Google Closure 的 @enum 标签保持兼容。
jsTry/** @enum {number} */constJSDocState = {BeginningOfLine : 0,SawAsterisk : 1,SavingComments : 2,};JSDocState .SawAsterisk ;
注意,@enum 与 TypeScript 的 enum 差别很大,也简单得多。不过,与 TypeScript 的枚举不同,@enum 的成员可以是任意类型:
jsTry/** @enum {function(number): number} */constMathFuncs = {add1 : (n ) =>n + 1,id : (n ) => -n ,sub1 : (n ) =>n - 1,};MathFuncs .add1 ;
@author
你可以用 @author 指定某个条目的作者:
tsTry/*** Welcome to awesome.ts* @author Ian Awesome <i.am.awesome@example.com>*/
记得用尖括号把电子邮件地址括起来。否则,@example 会被解析成一个新标签。
其他支持的写法
jsTryvarsomeObj = {/*** @param {string} param1 - JSDocs on property assignments work*/x : function (param1 ) {},};/*** As do jsdocs on variable assignments* @return {Window}*/letsomeFunc = function () {};/*** And class methods* @param {string} greeting The greeting to use*/Foo .prototype .sayHi = (greeting ) =>console .log ("Hi!");/*** And arrow function expressions* @param {number} x - A multiplier*/letmyArrow = (x ) =>x *x ;/*** Which means it works for function components in JSX too* @param {{a: string, b: number}} props - Some param*/varfc = (props ) => <div >{props .a .charAt (0)}</div >;/*** A parameter can be a class constructor, using Google Closure syntax.** @param {{new(...args: any[]): object}} C - The class to register*/functionregisterClass (C ) {}/*** @param {...string} p1 - A 'rest' arg (array) of strings. (treated as 'any')*/functionfn10 (p1 ) {}/*** @param {...string} p1 - A 'rest' arg (array) of strings. (treated as 'any')*/functionfn9 (p1 ) {returnp1 .join ();}
不支持的写法
对象字面量类型中,属性类型后面的等号后缀并不表示可选属性:
jsTry/*** @type {{ a: string, b: number= }}*/varwrong ;/*** Use postfix question on the property name instead:* @type {{ a: string, b?: number }}*/varright ;
可空类型只有在开启 strictNullChecks 时才有意义:
jsTry/*** @type {?number}* With strictNullChecks: true -- number | null* With strictNullChecks: false -- number*/varnullable ;
TypeScript 原生的写法是联合类型:
jsTry/*** @type {number | null}* With strictNullChecks: true -- number | null* With strictNullChecks: false -- number*/varunionNullable ;
不可空类型没有实际含义,会被当作其原始类型处理:
jsTry/*** @type {!number}* Just has type number*/varnormal ;
与 JSDoc 的类型系统不同,TypeScript 只允许你标记类型是否包含 null。它没有显式的不可空性——如果 strictNullChecks 开启,那么 number 就是不可空的;如果关闭,那么 number 就是可空的。
不支持的标签
TypeScript 会忽略任何不受支持的 JSDoc 标签。
以下标签有请求支持的未解决 issue:
@memberof(issue #7237)@yields(issue #23857)@member(issue #56674)
旧式类型同义词
为了兼容旧的 JavaScript 代码,许多常见类型都被赋予了别名。其中一些别名与现有类型相同,不过它们大多很少使用。例如,String 会被当作 string 的别名。尽管 String 在 TypeScript 中是一个类型,但旧的 JSDoc 常用它来表示 string。此外,在 TypeScript 中,原始类型的大写形式是包装类型——几乎总是误用。因此,编译器根据旧 JSDoc 中的用法,把这些类型视为同义词:
String -> stringNumber -> numberBoolean -> booleanVoid -> voidUndefined -> undefinedNull -> nullfunction -> Functionarray -> Array<any>promise -> Promise<any>Object -> anyobject -> any
当 noImplicitAny: true 时,最后四个别名会被关闭:
object和Object是内置类型,不过Object很少被使用。array和promise不是内置的,但可能在你程序的某处声明过。