注意:此页面已过期。如需查看完整列表,请访问 https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler
概览
Closure 编译器可以使用有关 JavaScript 变量的数据类型信息提供增强型优化和警告。但是,JavaScript 无法声明类型。
由于 JavaScript 没有用于声明变量类型的语法,因此您必须在代码中使用注释来指定数据类型。
Closure 编译器的类型语言派生自 JSDoc 文档生成工具使用的注解,但之后已相划分。它现在包含 JSDoc 不支持的多个注解,反之亦然。本文档介绍了 Closure 编译器能够理解的一组注解和类型表达式。
JSDoc 标记
Closure 编译器在 JSDoc 标记中查找类型信息。请使用以下参考表中所述的 JSDoc 标记来帮助编译器优化代码,并检查其是否存在可能的类型错误和其他错误。
此表格仅包含影响 Closure 编译器行为的标记。如需了解其他 JSDoc 标记,请参阅 JSDoc 工具包文档。
标记 | 说明 |
---|---|
@abstract
|
将方法标记为抽象。与将方法设置为
如果带有 /** @abstract */ foo.MyClass.prototype.abstractMethod = function() {}; |
@const
|
将变量标记为只读。编译器可以内嵌 类型声明是可选的。
如果为标记了 /** @const */ var MY_BEER = 'stout'; /** * My namespace's favorite kind of beer. * @const {string} */ mynamespace.MY_BEER = 'stout'; /** @const */ MyClass.MY_BEER = 'stout'; |
@constructor
|
将函数标记为构造函数。
编译器需要为 例如: /** * A rectangle. * @constructor */ function GM_Rect() { ... } |
@define
|
表示可在编译时由编译器替换的常量。
通过左侧示例,您可以将标记 --define='ENABLE_DEBUG=false' 传递给编译器,以将 ENABLE_DEBUG 的值更改为 false。定义的常量的类型可以是数字、字符串或布尔值。定义只能在全局范围内使用。
例如: /** @define {boolean} */ var ENABLE_DEBUG = true; /** @define {boolean} */ goog.userAgent.ASSUME_IE = false; |
@deprecated
|
标记函数、方法或属性,以便使用时会生成编译器警告,指出不应再使用。 例如: /** * Determines whether a node is a field. * @return {boolean} True if the contents of * the element are editable, but the element * itself is not. * @deprecated Use isField(). */ BN_EditUtil.isTopEditableField = function(node) { ... }; |
@dict
|
例如: /** * @constructor * @dict */ function Foo() {} var obj1 = new Foo(); obj1['x'] = 123; obj1.x = 234; // warning var obj2 = /** @dict */ { 'x': 321 }; obj2.x = 123; // warning |
@enum
|
指定枚举的类型。枚举是一个对象,其属性构成了一组相关常量。 枚举的类型标签适用于该枚举的每个属性。例如,如果枚举的类型为 例如: /** * Enum for tri-state values. * @enum {number} */ project.TriState = { TRUE: 1, FALSE: -1, MAYBE: 0 }; |
@export
|
提供此代码 /** @export */ foo.MyPublicClass.prototype.myPublicMethod = function() { // ... };
当编译器使用 goog.exportProperty(foo.MyPublicClass.prototype, 'myPublicMethod', foo.MyPublicClass.prototype.myPublicMethod); 这会将符号导出到未编译的代码。您可以将 /** * @export * @type {SomeType} */的简写形式 使用
|
@extends
|
将类或接口标记为从其他类继承。标有
注意:
如需查看继承的示例实现,请参阅 Closure 库函数 例如: /** * Immutable empty node list. * @constructor * @extends {goog.ds.BasicNodeList} */ goog.ds.EmptyNodeList = function() { ... }; |
@final
|
表示此类不可扩展。 对于方法,表示不允许任何子类替换该方法。 例如: /** * A class that cannot be extended. * @final * @constructor */ sloth.MyFinalClass = function() { ... } /** * A method that cannot be overridden. * @final */ sloth.MyFinalClass.prototype.method = function() { ... }; |
@implements
|
与
如果您使用 例如: /** * A shape. * @interface */ function Shape() {}; Shape.prototype.draw = function() {}; /** * @constructor * @implements {Shape} */ function Square() {}; Square.prototype.draw = function() { ... }; |
@implicitCast
|
此注解只能显示在 extern 属性声明中。该属性具有声明的类型,但您可以为它分配任何类型,而不显示警告。访问该属性时,您会获得所声明类型的值。例如,可以为 /** * @type {string} * @implicitCast */ Element.prototype.innerHTML; |
@inheritDoc
|
表示子类的方法或属性有意隐藏父类的方法或属性,并且具有完全相同的文档。请注意, 例如: /** @inheritDoc */ project.SubClass.prototype.toString = function() { ... }; |
@interface
|
将函数标记为接口。接口可指定某个类型的必需成员。任何实现接口的类都必须实现在该接口的原型上定义的所有方法和属性。请参阅
编译器验证接口是否未实例化。如果 例如: /** * A shape. * @interface */ function Shape() {}; Shape.prototype.draw = function() {}; /** * A polygon. * @interface * @extends {Shape} */ function Polygon() {}; Polygon.prototype.getSides = function() {}; |
@lends
|
表示对象字面量的键应被视为其他对象的属性。此注释应仅出现在对象字面量中。
请注意,大括号中的名称不是其他注解中的类型名称。它是对象名称。它会为属性所绑定的对象命名。
例如, 如需详细了解此注解,请参阅 JSDoc 工具包文档。 例如: goog.object.extend( Button.prototype, /** @lends {Button.prototype} */ ({ isButton: function() { return true; } })); |
@license 或 @preserve
|
告知编译器在标记文件的已编译代码之前插入关联的注释。此注解允许重要声明(例如法律许可或版权文本)在编译期间保持不变。系统会保留换行符。 例如: /** * @preserve Copyright 2009 SomeThirdParty. * Here is the full license text and copyright * notice for this file. Note that the notice can span several * lines and is only terminated by the closing star and slash: */ |
@nocollapse
|
表示不应由编译器收起为变量的属性。 例如: /** * A namespace. * @const */ var foo = {}; /** * @nocollapse */ foo.bar = 42; window['foobar'] = foo.bar; |
@nosideeffects
|
表示对声明的外部函数的调用没有副作用。
此注解允许编译器在未使用返回值时移除对函数的调用。该注释只能在 例如: /** @nosideeffects */ function noSideEffectsFn1() {} /** @nosideeffects */ var noSideEffectsFn2 = function() {}; /** @nosideeffects */ a.prototype.noSideEffectsFn3 = function() {}; |
@override
|
表示子类的方法或属性有意隐藏父类的方法或属性。如果不包含其他注解,则方法或属性会自动从其父类继承注解。 例如: /** * @return {string} Human-readable representation of * project.SubClass. * @override */ project.SubClass.prototype.toString = function() { ... }; |
@package
|
将成员或属性标记为私有。只有同一目录中的代码才能访问标记为
公共构造函数可以具有 例如: /** * Returns the window object the foreign document resides in. * * @return {Object} The window object of the peer. * @package */ goog.net.xpc.CrossPageChannel.prototype.getPeerWindowObject = function() { // ... }; |
@param
|
与方法、函数和构造函数定义搭配使用,以指定函数参数的类型。
或者,您也可以用内嵌方式为参数添加注解(请参阅示例中的函数 例如: /** * Queries a Baz for items. * @param {number} groupNum Subgroup id to query. * @param {string|number|null} term An itemName, * or itemId, or null to search everything. */ goog.Baz.prototype.query = function(groupNum, term) { ... }; function foo(/** number */ a, /** number */ b) { return a - b + 1; }对于具有销毁格式的参数,您可以在类型注解后面使用任何有效的 JS 标识符的名称。 /** * @param {{name: string, age: number}} person */ function logPerson({name, age}) { console.log(`${name} is ${age} years old`); } |
@private
|
将成员标记为私享。只有同一文件中的代码才能访问标记为
还可以随时随地访问标记为 例如: /** * Handlers that are listening to this logger. * @private {Array<Function>} */ this.handlers_ = []; |
@protected
|
表示成员或属性受到保护。
标有
例如: /** * Sets the component's root element to the given element. * Considered protected and final. * @param {Element} element Root element for the component. * @protected */ goog.ui.Component.prototype.setElementInternal = function(element) { // ... }; |
@record
|
将函数标记为结构接口。结构接口类似于名义 例如: /** * Anything with a draw() method. * @record */ function Drawable() {}; Drawable.prototype.draw = function() {}; /** * A polygon. * @param {!Drawable} x */ function render(x) { x.draw(); }; var o = { draw() { /* ... */ } }; render(o); |
@return
|
指定方法和函数定义的返回类型。
或者,您也可以以内嵌方式为返回值类型添加注解(请参阅示例中的函数
如果不在外部函数中的返回值没有,那么您可以省略 例如: /** * Returns the ID of the last item. * @return {string} The hex ID. */ goog.Baz.prototype.getLastId = function() { ... return id; }; function /** number */ foo(x) { return x - 1; } |
@struct
|
例如: /** * @constructor * @struct */ function Foo(x) { this.x = x; } var obj1 = new Foo(123); var someVar = obj1.x; // OK obj1.x = "qwerty"; // OK obj1['x'] = "asdf"; // warning obj1.y = 5; // warning var obj2 = /** @struct */ { x: 321 }; obj2['x'] = 123; // warning |
@template
|
请参阅通用类型。 例如: /** * @param {T} t * @constructor * @template T */ Container = function(t) { ... }; |
@this
|
指定关键字
为防止编译器发出警告,每当 例如: chat.RosterWidget.extern('getRosterElement', /** * Returns the roster widget element. * @this {Widget} * @return {Element} */ function() { return this.getComponent().getElement(); }); |
@throws
|
用于记录函数抛出的异常。 类型检查工具目前没有使用此信息。它仅用于确定 extern 文件中声明的函数是否会产生副作用。 例如: /** * @throws {DOMException} */ DOMApplicationCache.prototype.swapCache = function() { ... }; |
@type
|
标识变量、属性或表达式的类型。 声明变量或函数参数时,您可以编写内嵌代码(省略 例如: /** * The message hex ID. * @type {string} */ var hexId = hexId; var /** string */ name = 'Jamie'; function useSomething(/** (string|number|!Object) */ something) { ... } |
@typedef
|
为更复杂的类型声明别名。 目前,类型定义只能在顶层定义,不能在函数内部定义。我们修复了新类型推断中的这一限制。 例如: /** @typedef {(string|number)} */ goog.NumberLike; /** @param {goog.NumberLike} x A number or a string. */ goog.readNumber = function(x) { ... } |
@unrestricted
|
表示某个类既不是 例如: /** * @constructor * @unrestricted */ function Foo(x) { this.x = x; } var obj1 = new Foo(123); var someVar = obj1.x; // OK obj1.x = "qwerty"; // OK obj1['x'] = "asdf"; // OK obj1.y = 5; // OK |
类型表达式
您可以使用类型表达式指定任何变量、属性、表达式或函数参数的数据类型。类型表达式由大括号(“{ }”)构成,大括号中包含下述类型运算符的部分组合。
使用类型表达式和 @param
标记来声明函数参数的类型,使用类型表达式和 @type
标记来声明变量、属性或表达式的类型。
您在代码中指定的类型越多,编译器可以执行的优化就越多,可发现的错误就越多。
编译器使用这些注解对程序进行类型检查。
请注意,Closure 编译器不保证一定能理解程序中每个表达式的类型。系统会尽最大努力查看变量的使用方式,以及附加到其声明的类型注解。然后,它会使用多种类型推断算法计算出尽可能多的表达式的类型。其中一些算法很简单(“如果 x 是一个数字,并且我们看到 y = x;
,那么 y 是一个数字)。有些是更间接的代码(“如果 f 的第一个参数记录为必须接受数字的回调,并且我们看到 f(function(x) { /** ... */ });
,那么 x 必须是数字)。
运算符名称 | 语法示例 | 说明 |
---|---|---|
类型名称 |
{boolean} {Window} {goog.ui.Menu}
|
指定类型的名称。 |
类型:应用 |
{Array<string>} 字符串数组。
|
使用一组类型参数对类型进行参数化。类似于 Java 泛型。 |
类型联合 |
{(number|boolean)} 数字或布尔值。 请注意括号,这是必需的。 |
表示某个值可能具有 A 类型或 B 类型。 |
记录类型 |
{{myNum: number, myObject}}
具有名为 myNum 的属性(值为 number 的类型)和名为 myObject 的属性(具有任何类型的值)的匿名类型。 |
表示该值具有具有指定类型值的指定成员。 大括号是类型语法的一部分。例如,如需表示具有 |
可为 null 类型 |
{?number} 数字或 null 。
|
表示值为 A 或 默认情况下,无论是否使用 Nullable 运算符声明这些对象类型,它们均可为 null。对象类型是指除函数、字符串、数字或布尔值外的任何内容。如需将对象类型设为不可为 null,请使用不可为 null 运算符。 |
不可为 null 类型 |
{!Object} 对象,但绝不是 null 值。
|
表示值类型为 A 而不是 null。 默认情况下,无论函数和所有值类型(布尔值、数字和字符串)是否使用不可为 null 的运算符进行声明,它们都不可为 null。如需将值或函数类型设为可为 null,请使用 Nullable 运算符。 |
函数类型 |
{function(string, boolean)} 接受两个参数(一个字符串和一个布尔值)且返回值未知的函数。 |
指定函数及其函数类型。 |
函数返回值类型 |
{function(): number} 不接受任何参数并返回数字的函数。 |
指定函数的返回值的类型。 |
函数 this 类型 |
{function(this:goog.ui.Menu, string)} 该函数接受一个参数(字符串),并在 goog.ui.Menu 中执行。 |
指定函数中 this 的值的类型。 |
函数 new 类型 |
{function(new:goog.ui.Menu, string)} 该函数接受一个参数(字符串),并使用“new”关键字调用时创建新的 goog.ui.Menu 实例。 |
指定构造函数的构造类型。 |
变量参数 |
{function(string, ...number): number} 该函数接受一个参数(一个字符串),后跟一个可变数量(必须为数字)的参数。 |
表示函数类型接受可变数量的参数,并为变量参数指定类型。 |
变量参数(在 @param 注解中)
|
@param {...number} var_args 带有注解的函数的可变数量的参数。 |
表示带注释的函数接受可变数量的参数,并为变量参数指定类型。 |
@param 注解中的可选参数
|
@param {number=} opt_argument 类型为 number 的可选参数。
|
表示由
如果方法调用省略了可选参数,则该参数的值为 /** * Some class, initialized with an optional value. * @param {Object=} opt_value Some value (optional). * @constructor */ function MyClass(opt_value) { /** * Some value. * @type {Object|undefined} */ this.myValue = opt_value; } |
函数类型中的可选参数 |
{function(?string=, number=)} 将一个可选且可为 null 的字符串和一个可选数字作为参数的函数。 |
表示函数类型中的参数是可选的。函数调用中可以省略可选参数。可选参数不能放在参数列表中的非可选参数之前。 |
ALL 类型 | {*} |
表示该变量可采用任何类型。 |
UNKNOWN 类型 | {?} |
表示变量可采用任何类型,编译器不应对其类型进行任何类型检查。 |
类型转换
要将值的类型转换为特定类型,请使用以下语法
/** @type {!MyType} */ (valueExpression)表达式的圆括号始终是必填项。
通用类型
与 Java 非常相似,Closure 编译器支持通用类型、函数和方法。泛型可处理各种类型的对象,同时保持编译时类型的安全性。
您可以使用泛型来实现泛型集合,以存储对特定类型的对象的引用,以及对特定类型的对象执行操作的泛化算法。
声明通用类型
通过将 @template
注解添加到类型的构造函数(对于类)或接口声明(对于接口),可以使类型变为通用类型。例如:
/** * @constructor * @template T */ Foo = function() { ... };
注释 @template T
表示 Foo
是一种通用类型,具有一个模板类型 T
。模板类型 T
可用作 Foo
定义范围内的类型。例如:
/** @return {T} */ Foo.prototype.get = function() { ... }; /** @param {T} t */ Foo.prototype.set = function(t) { ... };
方法 get
将返回一个 T
类型的对象,并且 set
方法仅接受 T
类型的对象。
实例化通用类型
通过重复使用上面的示例,您可以通过多种方式创建 Foo
的模板实例:
/** @type {!Foo<string>} */ var foo = new Foo(); var foo = /** @type {!Foo<string>} */ (new Foo());
上述两种构造函数语句都会创建Foo
实例,其模板类型 T
为 string
。编译器会遵循模板化类型,强制调用 foo
的方法以及访问 foo
的属性。例如:
foo.set("hello"); // OK. foo.set(3); // Error - expected a string, found a number. var x = foo.get(); // x is a string.
实例的构造函数参数也可以隐式输入。考虑其他通用类型 Bar
:
/** * @param {T} t * @constructor * @template T */ Bar = function(t) { ... }; var bar = new Bar("hello"); // bar is a Bar<string>
Bar
构造函数的参数类型会推断为 string
,因此创建的实例 bar
会推断为 Bar<string>
。
多个模板类型
通用模板可拥有任意数量的模板类型。以下映射类有两种模板类型:
/** * @constructor * @template Key, Val */ MyMap = function() { ... };
通用类型的所有模板类型必须在同一 @template
注解中指定为英文逗号分隔列表。模板类型名称的顺序很重要,因为模板化类型注释将使用排序规则将模板类型与值配对。例如:
/** @type {MyMap<string, number>} */ var map; // Key = string, Val = number.
泛型类型的不变性
Closure 编译器强制执行不变的通用类型。这意味着,如果上下文需要 Foo<X>
类型,则当 X
和 Y
是不同的类型时,您不能传递 Foo<Y>
类型,即使其中一个是另一个的子类型也是如此。例如:
/** * @constructor */ X = function() { ... }; /** * @extends {X} * @constructor */ Y = function() { ... }; /** @type {Foo<X>} */ var fooX; /** @type {Foo<Y>} */ var fooY; fooX = fooY; // Error fooY = fooX; // Error /** @param {Foo<Y>} fooY */ takesFooY = function(fooY) { ... }; takesFooY(fooY); // OK. takesFooY(fooX); // Error
泛型类型的继承
通用类型可以继承,其模板类型既可以是固定的,也可以传播到继承类型。以下示例展示了一个继承类型,用于修复其父类型的模板类型:
/** * @constructor * @template T */ A = function() { ... }; /** @param {T} t */ A.prototype.method = function(t) { ... }; /** * @constructor * @extends {A<string>} */ B = function() { ... };
通过扩展 A<string>
,B
将具有 method
方法,该方法采用 string
类型的参数。
以下示例展示了继承其超类型的模板类型的继承类型:
/** * @constructor * @template U * @extends {A<U>} */ C = function() { ... };
通过扩展 A<U>
,C
的模板实例将具有 method
方法,该方法接受模板类型 U
的参数。
接口可以以类似的方式实现和扩展,但单个类型无法使用不同的模板类型多次实现同一接口。例如:
/** * @interface * @template T */ Foo = function() {}; /** @return {T} */ Foo.prototype.get = function() {}; /** * @constructor * @implements {Foo<string>} * @implements {Foo<number>} */ FooImpl = function() { ... }; // Error - implements the same interface twice
通用函数和方法
与通用类型类似,可以通过将 @template
注解添加到定义中来使函数和方法成为通用类型。例如:
/** * @param {T} a * @return {T} * @template T */ identity = function(a) { return a; }; /** @type {string} */ var msg = identity("hello") + identity("world"); // OK /** @type {number} */ var sum = identity(2) + identity(2); // OK /** @type {number} */ var sum = identity(2) + identity("2"); // Type mismatch