為 Closure 編譯器註解 JavaScript

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

總覽

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

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

Closure 編譯器的型別語言衍生自 JSDoc 文件產生工具使用的註解,但兩者後來有所差異。現在包含 JSDoc 不支援的註解,反之亦然。本文說明 Closure 編譯器可解讀的註解和型別運算式。

  1. JSDoc 標記
  2. 型別運算式
  3. 泛型

JSDoc 標記

Closure 編譯器會在 JSDoc 標記中尋找型別資訊。使用下表參考資料中說明的 JSDoc 標記,協助編譯器最佳化程式碼,並檢查可能的型別錯誤和其他錯誤。

這個表格只會列出影響 Closure Compiler 行為的標記。如要瞭解其他 JSDoc 標記,請參閱 JSDoc Toolkit 說明文件

標記 說明
@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 程式庫函式 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 識別項名稱。
/**
 * @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 出現在既非原型方法,也非標示為 @constructor 的函式中時,您都必須使用 @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

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

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

表示類別既不是 @struct 型別, 也不是 @dict 型別。這是預設值,因此一般來說不必明確編寫,除非您使用 class 關鍵字,這兩者預設都會產生 @struct 類別。

例如:

/**
 * @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 型別,且不得為空值。

無論函式和所有值型別 (布林值、數字和字串) 是否以 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 的選用參數。

指出 @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=)}
這個函式會將一個選用的可為空值字串和一個選用的數字做為引數。
表示函式型別中的引數為選用項目。函式呼叫可以省略選用引數。在引數清單中,選用引數不得位於必要引數之前。
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());

上述兩個建構函式陳述式都會建立範本類型為 stringFoo 執行個體。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> 型別,即使 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