為 Closure Compiler 撰寫 JavaScript

注意:這個頁面已過時。完整清單請參閱 https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler

總覽

Closure Compiler 可以使用 JavaScript 變數的資料類型資訊,以提供更完善的最佳化和警告功能。然而,JavaScript 無法宣告類型。

由於 JavaScript 沒有宣告變數類型的語法,因此您必須使用程式碼中的註解來指定資料類型。

Closure Compiler 的類型衍生自 JSDoc 文件產生工具所使用的註解,但此後已發展更多元。 目前包含 JSDoc 不支援的數個註解,反之亦然。本文件說明 Closure Compiler 能夠解讀的註解集和類型運算式。

  1. JSDoc 廣告代碼
  2. 類型運算式
  3. 通用類型

JSDoc 廣告代碼

Closure Compiler 會在 JSDoc 標記中尋找類型資訊。請使用下方參照表格所述的 JSDoc 標記,協助編譯器將程式碼最佳化,並檢查是否有可能的類型錯誤和其他錯誤。

這個表格只包含影響 Closure Compiler 行為的標記。如需其他 JSDoc 標記的相關資訊,請參閱 JSDoc 工具包說明文件

標記 說明
@abstract

將方法標示為抽象。類似於將方法設為 goog.abstractMethod,編譯器可以修剪帶有 @abstract 註解的方法來縮減程式碼大小。

如果標有 @abstract 的方法有非空白實作,編譯器會產生警告。

例如:
/** @abstract */
foo.MyClass.prototype.abstractMethod = function() {};
@const

將變數標示為唯讀狀態。編譯器可以內嵌 @const 變數,這個變數可將 JavaScript 程式碼最佳化。

類型宣告是選用項目。

如果多次標示 @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

將函式標示為建構函式。編譯器需要與 new 關鍵字搭配使用的任何函式使用 @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

@dict 可用來建立具有可變屬性數量的物件。當建構函式 (範例中的 Foo) 加上 @dict 註解時,您只能使用括號標記法來存取 Foo 物件的屬性。註解也可以直接用於物件常值。

例如:

/**
 * @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 標記後面必須加上類型運算式

列舉的類型標籤適用於列舉的每個屬性。舉例來說,如果列舉的類型是 number,則其每個列舉的屬性都必須為數值。如果省略列舉類型,系統會採用 number

例如:

/**
 * Enum for tri-state values.
 * @enum {number}
 */
project.TriState = {
  TRUE: 1,
  FALSE: -1,
  MAYBE: 0
};
@export

指定此代碼

/** @export */
foo.MyPublicClass.prototype.myPublicMethod = function() {
  // ...
};

編譯器使用 --generate_exports 標記執行時,會產生以下程式碼:

goog.exportProperty(foo.MyPublicClass.prototype, 'myPublicMethod',
  foo.MyPublicClass.prototype.myPublicMethod);

這樣會將符號匯出成未編譯的程式碼。您可以將 /** @export {SomeType} */ 寫成

/**
 * @export
 * @type {SomeType}
 */
的簡寫

使用 @export 註解的程式碼必須

  1. 包括closure/base.js,或
  2. 在其程式碼集中使用相同方法簽章定義 goog.exportSymbolgoog.exportProperty
@extends

將類別或介面標示為繼承自其他類別。標有 @extends 的類別也必須加上 @constructor@interface 標示。

注意事項:@extends 不會導致類別繼承其他類別。該註解只會告訴編譯器,在類型檢查期間,可將編譯器視為一個類別作為另一個類別的子類別。

如需繼承實作範例,請參閱 Closure Library 函式 goog.inherits()

例如:

/**
 * 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

可與 @constructor 搭配使用,表示類別實作了一個介面。

如果您使用 @implements 標記建構函式,卻無法實作介面定義的所有方法和屬性,編譯器就會發出警告。

例如:


/**
 * A shape.
 * @interface
 */
function Shape() {};
Shape.prototype.draw = function() {};

/**
 * @constructor
 * @implements {Shape}
 */
function Square() {};
Square.prototype.draw = function() {
  ...
};
@implicitCast

此註解只能出現在外部屬性宣告中。 屬性擁有已宣告的類型,但您可以指派該類型給任何類型,而不顯示警告。存取屬性時,您會取得所宣告類型的值。舉例來說,element.innerHTML 可以指派任何類型,但一律會傳回字串。

/**
 * @type {string}
 * @implicitCast
 */
Element.prototype.innerHTML;
@inheritDoc

指出子類別的方法或屬性刻意隱藏父類別的方法或屬性,且完全相同。請注意,@inheritDoc 標記暗示的是 @override 標記。

例如:

/** @inheritDoc */
project.SubClass.prototype.toString = function() {
  ...
};
@interface

將函式標示為介面。介面會指定類型所需的成員。實作實作介面的任何類別都必須實作該介面原型中定義的所有方法和屬性。請見 @implements

編譯器會驗證介面未執行個體化。如果 new 關鍵字與介面函式搭配使用,編譯器會產生警告。

例如:

/**
 * A shape.
 * @interface
 */
function Shape() {};
Shape.prototype.draw = function() {};

/**
 * A polygon.
 * @interface
 * @extends {Shape}
 */
function Polygon() {};
Polygon.prototype.getSides = function() {};
@lends

表示應將物件常值的索引鍵視為其他物件的屬性。這個註解只能出現在物件常值上。

請注意,大括號中的名稱不如同其他註解中的類型名稱。這是物件名稱。以屬性為屬性所命名的物件。例如,@type {Foo} 表示「Foo 執行個體」,@lends {Foo} 則代表「建構函式 Foo」。

如要進一步瞭解這項註解,請參閱 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

表示不應由編譯器收合至變數的屬性。@nocollapse 的主要用途是允許匯出可變動的屬性。請注意,未收合的屬性仍可由編譯器重新命名。如果您使用 @nocollapse 來為物件加註,則其所有屬性也會保持收合狀態。

例如:

/**
 * A namespace.
 * @const
 */
var foo = {};

/**
 * @nocollapse
 */
foo.bar = 42;

window['foobar'] = foo.bar;
@nosideeffects

指出所宣告外部函式的呼叫沒有任何副作用。如果未傳回傳回值,這個註解可讓編譯器移除對函式的呼叫。這個註解僅適用於 extern files

例如:

/** @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

將成員或屬性標示為不公開。只有相同目錄中的程式碼才能存取名為 @package 的名稱。特別是父項和子項目錄中的程式碼無法存取 @package 的名稱。

公用建構函式可以有 @package 屬性,以限制在目錄外的呼叫端可使用的方法。另一方面,@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

可與方法、函式和建構函式定義搭配使用,以指定函式引數的類型。@param 標記的順序必須與函式定義中的參數相同。

@param 標記後面必須加上類型運算式

或者,您可以在內嵌註解參數類型 (請參閱範例中的 foo 函式)。

例如:


/**
 * 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

將成員標示為私人。只有同一個檔案中的程式碼可以存取標示為 @private 的全域變數和函式。標示為 @private 的建構函式只能透過同一個檔案中的程式碼以及其靜態和執行個體成員執行個體化。

您也可以在任何位置存取標示 @private 的建構函式的公開靜態屬性,而 instanceof 運算子可以隨時存取 @private 成員。

例如:

/**
 * Handlers that are listening to this logger.
 * @private {Array<Function>}
 */
this.handlers_ = [];

@protected

表示成員或屬性受到保護。

可存取 @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

將函式標示為結構介面。結構介面與傳統 @interface 類似,但允許隱含實作。這表示任何包含結構介面原型定義方法和屬性的類別,都會使用結構介面,不論其是否使用 @implements 標記。如果記錄類型和物件常值含有必要屬性,也會以隱含方式實作結構介面。

例如:

/**
 * 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

指定方法和函式定義的傳回類型。@return 標記後面必須加上類型運算式

或者,您可以使用內嵌方式為傳回類型加上註解 (請參閱範例中的 foo 函式)。

如果不在外部範圍的函式沒有傳回值,您可以省略 @return 標記,編譯器會假設函式會傳回 undefined

例如:

/**
 * 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

@struct 是用來建立具有固定數量屬性的物件。當建構函式 (範例中的 Foo) 加上 @struct 註解時,您只能使用點標記法存取 Foo 物件的屬性,不能使用括號標記法。此外,屬性一經建立即無法新增至 Foo 執行個體。註解也可以直接用於物件常值。

例如:

/**
 * @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

指定 this 關鍵字在函式中參照的物件類型。@this 標記後面必須加上類型運算式

為避免編譯器警告,只要 this 出現在非 prototype 方法或標示為 @constructor 的函式中,您都必須使用 @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

識別變數、屬性或運算式的類型。@type 標記後面必須加上類型運算式

宣告變數或函式參數時,您可以編寫類型註解,以省略 {}@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

指出類別不是 @struct 類型,也不是 @dict 類型。

例如:

/**
 * @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>}
字串陣列。

{Object<string, number>}
其中鍵為字串且值為數字的物件。

使用一組類型引數將類型參數化。類似 Java 泛型。
輸入聯集 {(number|boolean)}
數字或布林值。

請留意必要的括號。
表示值可能為 A 類型或 B 類型。
紀錄類型 {{myNum: number, myObject}}
匿名類型,具有名為 myNum 的屬性,其值為 number 類型,以及名為 myObject 且具有任何類型值的屬性。

表示該值具有指定成員,且具有指定類型的值。

括號是類型語法的一部分。舉例來說,如要表示具備 length 屬性的物件 Array,您可以寫入:
Array<{length}>。在左上方範例中,外部大括號表示這是類型運算式,內部大括號表示這是記錄類型。

可為空值類型 {?number}
一個數字或 null

表示值為 A 或 null 類型。

所有物件類型都會預設為空值,無論這些物件是否使用 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 類型的選用參數。

表示 @param 註解所描述的引數為選用引數。函式呼叫可以省略選用引數。選用參數前面不能是參數清單中的非選用參數。

如果方法呼叫省略了選用參數,該引數的值會是 undefined。因此,如果該方法將參數值儲存在類別屬性中,則該屬性的類型宣告必須包含 undefined 的可能值,如以下範例所示:

/**
 * 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 執行個體,其範本類型 Tstring。編譯器會強制執行對 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> 類型,則當 XY 是不同的類型時,即使類型為另一個類型的子類型,也無法傳遞類型 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