注意:這個頁面已過時。完整清單請參閱 https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler
總覽
Closure Compiler 可以使用 JavaScript 變數的資料類型資訊,以提供更完善的最佳化和警告功能。然而,JavaScript 無法宣告類型。
由於 JavaScript 沒有宣告變數類型的語法,因此您必須使用程式碼中的註解來指定資料類型。
Closure Compiler 的類型衍生自 JSDoc 文件產生工具所使用的註解,但此後已發展更多元。 目前包含 JSDoc 不支援的數個註解,反之亦然。本文件說明 Closure Compiler 能夠解讀的註解集和類型運算式。
JSDoc 廣告代碼
Closure Compiler 會在 JSDoc 標記中尋找類型資訊。請使用下方參照表格所述的 JSDoc 標記,協助編譯器將程式碼最佳化,並檢查是否有可能的類型錯誤和其他錯誤。
這個表格只包含影響 Closure Compiler 行為的標記。如需其他 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 Library 函式 例如: /** * 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; }如果是具破壞性的模式參數,您可以在類型註解後方使用任何有效的 JS ID 名稱。 /** * @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
|
用於記錄函式擲回的例外狀況。類型檢查工具目前未使用這項資訊。 該函式只會用來判斷在外部檔案所宣告的函式是否具有副作用。 例如: /** * @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
|
宣告較複雜類型的別名。目前,typedefs 只能在頂層定義,無法在函式內部定義。我們已在新類型推論中修正這個問題。 例如: /** @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 型,而不是空值。 根據預設,函式和所有值類型 (布林值、數字和字串) 都無法為空值,無論這些函式是否使用不可為空值運算子宣告。如要將值或函式類型設為空值,請使用 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=)} 一個函式將一個選用的空值字串和一個選用數字做為引數。 |
表示函式類型的引數為選用引數。函式呼叫可省略選用引數。選擇性引數的前面不能是引數清單中的非選用引數。 |
全部類型 | {*} |
表示變數可以採用任何類型。 |
UNKNOWN 類型 | {?} |
表示變數可以採用任何類型,而編譯器不應對類型的任何用途進行型式檢查。 |
類型投放
如果要將值轉換為特定類型,請使用以下語法
/** @type {!MyType} */ (valueExpression)表式前後應加上括號。
通用類型
與 Java 類似,Closure Compiler 支援一般類型、函式和方法。「一般」應用程式會針對多種類型的物件運作,同時維持編譯時間類型的安全性。
您可以使用泛型執行通用集合,其中包含特定類型物件的參照,以及透過特定類型物件運作的通用演算法。
宣告一般類型
您可以在類型建構函式 (類別) 或介面宣告 (介面) 中加入 @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 Compiler 會強制使用不變的一般類型。這表示,如果內容預期為 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