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 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 を使用しても、クラスが別のクラスから継承されることはありません。このアノテーションは、型チェック中に 1 つのクラスを別のクラスのサブクラスとして扱うことができることをコンパイラに伝えるだけです。

継承の実装例については、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

このアノテーションは externs プロパティ宣言でのみ使用できます。プロパティには宣言された型がありますが、警告なしで任意の型を割り当てることができます。プロパティにアクセスすると、宣言された型の値が返されます。たとえば、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 Toolkit のドキュメントをご覧ください。

次に例を示します。

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 を参照)。

externs にない関数に戻り値がない場合は、@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 タグの後に型式を続ける必要があります。

コンパイラの警告を防ぐには、プロトタイプ メソッドでも @constructor としてマークされた関数でもない関数に this が現れるたびに、@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 タグの後に型式を指定する必要があります。

変数または関数パラメータを宣言するときに、2 番目の例のように {}@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 キーワードを使用している場合は、明示的に記述する必要があります。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}
型の名前を指定します。
Type Application {Array<string>}
文字列の配列。

{Object<string, number>}
キーが文字列で、値が数値のオブジェクト。

型引数のセットを使用して型をパラメータ化します。Java のジェネリクスに似ています。
型 Union {(number|boolean)}
数値またはブール値。

括弧は必須です。
値が型 A または型 B の可能性があることを示します。
レコードタイプ {{myNum: number, myObject}}
値の型が numbermyNum という名前のプロパティと、値の型が任意の型の myObject という名前のプロパティの両方を持つ匿名型。

値に、指定された型の値を持つ指定されたメンバーがあることを示します。

中かっこは型構文の一部です。たとえば、length プロパティを持つオブジェクトの Array を示すには、
Array<{length}> と記述します。左側の例では、外側の中かっこは型式であることを示し、内側の中かっこはレコード型であることを示しています。

null 許容型 {?number}
数字または null

値が A 型または null であることを示します。

すべてのオブジェクト型は、Nullable 演算子で宣言されているかどうかに関係なく、デフォルトで null 許容です。オブジェクト型は、関数、文字列、数値、ブール値以外のものとして定義されます。オブジェクト型を null 許容型にするには、null 許容型演算子を使用します。

null 許容でない型 {!Object}
オブジェクト。ただし、null 値は使用されません。

値が型 A で null ではないことを示します。

関数とすべての値の型(ブール値、数値、文字列)は、Non-nullable 演算子で宣言されているかどうかに関係なく、デフォルトで null 非許容です。値または関数型を null 値許容にするには、Nullable 演算子を使用します。

関数タイプ {function(string, boolean)}
2 つのパラメータ(文字列とブール値)を受け取り、戻り値が不明な関数。
関数と関数のパラメータの型を指定します。
関数の戻り値の型 {function(): number}
パラメータを受け取らず、数値を返す関数。
関数の戻り値の型を指定します。
関数 this {function(this:goog.ui.Menu, string)}
1 つのパラメータ(文字列)を受け取り、goog.ui.Menu のコンテキストで実行される関数。
関数内の this の値の型を指定します。
関数 new {function(new:goog.ui.Menu, string)}
1 つのパラメータ(文字列)を受け取り、'new' キーワードで呼び出されたときに goog.ui.Menu の新しいインスタンスを作成する関数。
コンストラクタの構築型を指定します。
変数パラメータ {function(string, ...number): number}
1 つのパラメータ(文字列)と、数値である必要がある可変数のパラメータを受け取る関数。
関数型が可変数のパラメータを受け取ることを示し、可変パラメータの型を指定します。
変数パラメータ(@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=)}
省略可能な null 許容文字列と省略可能な数値を引数として受け取る関数。
関数型の引数が省略可能であることを示します。省略可能な引数は、関数呼び出しから省略できます。省略可能な引数は、引数リストで省略可能でない引数の前に置くことはできません。
ALL タイプ {*} 変数が任意の型を取ることができることを示します。
UNKNOWN 型 {?} 変数が任意の型を取ることができ、コンパイラがその使用を型チェックしないことを示します。

型キャスト

値を特定の型にキャストするには、次の構文を使用します。

/** @type {!MyType} */ (valueExpression)
式の周りの括弧は常に必要です。

ジェネリック型

Java と同様に、Closure Compiler は汎用型、関数、メソッドをサポートしています。ジェネリクスは、コンパイル時の型安全性を維持しながら、さまざまな型のオブジェクトを操作します。

ジェネリクスを使用すると、特定の型のオブジェクトへの参照を保持する汎用コレクションと、特定の型のオブジェクトに対して動作する汎用アルゴリズムを実装できます。

総称型を宣言する

型のコンストラクタ(クラスの場合)またはインターフェース宣言(インターフェースの場合)に @template アノテーションを追加することで、型をジェネリックにできます。次に例を示します。

/**
 * @constructor
 * @template T
 */
Foo = function() { ... };

アノテーション @template T は、Foo が 1 つのテンプレート型 T を持つジェネリック型であることを示します。テンプレート タイプ T は、Foo の定義のスコープ内でタイプとして使用できます。次に例を示します。

/** @return {T} */
Foo.prototype.get = function() { ... };

/** @param {T} t */
Foo.prototype.set = function(t) { ... };

メソッド getT 型のオブジェクトを返し、メソッド setT 型のオブジェクトのみを受け入れます。

ジェネリック型のインスタンス化

上記の例を再利用すると、Foo のテンプレート インスタンスは次のようないくつかの方法で作成できます。

/** @type {!Foo<string>} */ var foo = new Foo();
var foo = /** @type {!Foo<string>} */ (new Foo());

上記のコンストラクタ ステートメントはどちらも、テンプレート型 Tstring である Foo インスタンスを作成します。コンパイラは、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 と推論され、その結果、作成されたインスタンス barBar<string> と推論されます。

複数のテンプレート タイプ

ジェネリックには任意の数のテンプレート タイプを指定できます。次のマップクラスには、2 つのテンプレート タイプがあります。

/**
 * @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 には string 型のパラメータを受け取る method メソッドが追加されます。

次に、スーパークラスのテンプレート型を伝播する継承型の例を示します。

/**
 * @constructor
 * @template U
 * @extends {A<U>}
 */
C = function() { ... };

A<U> を拡張すると、C のテンプレート インスタンスには、テンプレート型 U のパラメータを受け取る method メソッドが追加されます。

インターフェースは同様の方法で実装および拡張できますが、1 つの型で異なるテンプレート型を使用して同じインターフェースを複数回実装することはできません。次に例を示します。

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