注意:本頁內容已過時。完整清單請參閱: https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler
總覽
Closure Compiler 可以使用 JavaScript 變數的資料型別資訊,提供更完善的最佳化和警告。但 JavaScript 無法宣告型別。
由於 JavaScript 沒有宣告變數類型的語法,您必須在程式碼中使用註解來指定資料類型。
Closure 編譯器的型別語言衍生自 JSDoc 文件產生工具使用的註解,但兩者後來有所差異。現在包含 JSDoc 不支援的註解,反之亦然。本文說明 Closure 編譯器可解讀的註解和型別運算式。
JSDoc 標記
Closure 編譯器會在 JSDoc 標記中尋找型別資訊。使用下表參考資料中說明的 JSDoc 標記,協助編譯器最佳化程式碼,並檢查可能的型別錯誤和其他錯誤。
這個表格只會列出影響 Closure Compiler 行為的標記。如要瞭解其他 JSDoc 標記,請參閱 JSDoc Toolkit 說明文件。
標記 | 說明 |
---|---|
@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
|
這項註解只能出現在外部屬性宣告中。
屬性已宣告類型,但您可以將任何類型指派給屬性,不會收到警告。存取屬性時,您會取得宣告型別的值。舉例來說, /** * @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; } /** * @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
|
用於記錄函式擲回的例外狀況。型別檢查器目前不會使用這項資訊。 這項資訊只會用於判斷 externs 檔案中宣告的函式是否具有副作用。 例如: /** * @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,不能在函式內定義。我們已在新類型推論中修正這項限制。 例如: /** @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 Compiler 無法保證能判斷程式中每個運算式的型別。並盡可能根據變數的使用方式,以及附加至變數宣告的型別註解,接著,它會使用多種型別推斷演算法,盡可能找出多個運算式的型別。部分演算法很簡單 (「如果 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 的屬性 (值為任何型別)。
|
指出值具有指定成員,且這些成員的值屬於指定型別。 大括號是型別語法的一部分。舉例來說,如要表示具有 |
可為空值的型別 |
{?number} 數字或 null 。
|
表示值為 A 類型或 無論物件型別是否以 Nullable 運算子宣告,預設都是可為空值。物件型別定義為函式、字串、數字或布林值以外的任何項目。如要將物件型別設為不可為空值,請使用 Non-nullable 運算子。 |
不可為空值的型別 |
{!Object} 物件,但絕不會是 null 值。
|
表示值為 A 型別,且不得為空值。 無論函式和所有值型別 (布林值、數字和字串) 是否以 Non-nullable 運算子宣告,預設都不可為空值。如要讓值或函式型別可為空值,請使用 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=)} 這個函式會將一個選用的可為空值字串和一個選用的數字做為引數。 |
表示函式型別中的引數為選用項目。函式呼叫可以省略選用引數。在引數清單中,選用引數不得位於必要引數之前。 |
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());
上述兩個建構函式陳述式都會建立範本類型為 string
的 Foo
執行個體。T
編譯器會強制呼叫 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