Como anotar JavaScript para o Compiler Compiler

Observação: esta página está desatualizada. A lista completa é mantida em https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-closure-Compiler

Visão geral

O Frontend Compiler pode usar informações de tipos de dados sobre variáveis JavaScript para fornecer otimização e avisos aprimorados. No entanto, o JavaScript não tem como declarar tipos.

Como o JavaScript não tem sintaxe para declarar o tipo de variável, você precisa usar comentários no código para especificar o tipo de dado.

A linguagem do tipo do Compile Compiler deriva das anotações usadas pela ferramenta de geração de documentos JSDoc, embora ela tenha divergido desde então. Agora, ele inclui várias anotações não suportadas pelo JSDoc e vice-versa. Este documento descreve o conjunto de anotações e expressões de tipo que o Frontend Compiler entende.

  1. Tags JSDoc
  2. Expressões de tipo (link em inglês)
  3. Tipos genéricos

Tags JSDoc

O closure Compiler procura informações de tipo nas tags JSDoc. Use as tags JSDoc descritas na tabela de referência abaixo para ajudar o compilador a otimizar seu código e verificar possíveis erros de tipo e outros erros.

Esta tabela inclui apenas tags que afetam o comportamento do Frontend Compiler. Para mais informações sobre outras tags JSDoc, consulte a documentação do kit de ferramentas do JSDoc.

Tag Descrição
@abstract

Marca um método como abstrato. De forma semelhante à definição de um método como goog.abstractMethod, o compilador pode remover métodos anotados com @abstract para reduzir o tamanho do código.

O compilador vai produzir um aviso se um método marcado com @abstract tiver uma implementação não vazia.

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

Marca uma variável como somente leitura. O compilador pode inserir variáveis @const in-line, o que otimiza o código JavaScript.

A declaração de tipo é opcional.

O compilador vai produzir um aviso se uma variável marcada com @const receber um valor mais de uma vez. Se a variável for um objeto, o compilador não proibirá mudanças nas propriedades do objeto.

Por exemplo:
/** @const */ var MY_BEER = 'stout';

/**
 * My namespace's favorite kind of beer.
 * @const {string}
 */
mynamespace.MY_BEER = 'stout';

/** @const */ MyClass.MY_BEER = 'stout';
@constructor

Marca uma função como um construtor. O compilador exige uma anotação @constructor para qualquer função usada com a palavra-chave new.

Exemplo:

/**
 * A rectangle.
 * @constructor
 */
function GM_Rect() {
  ...
}
@define Indica uma constante que pode ser substituída pelo compilador durante a compilação. Com o exemplo à esquerda, você pode transmitir a sinalização --define='ENABLE_DEBUG=false' para o compilador para alterar o valor de ENABLE_DEBUG para false. O tipo de uma constante definida pode ser número, string ou booleano. As definições só são permitidas no escopo global.

Exemplo:

/** @define {boolean} */
var ENABLE_DEBUG = true;

/** @define {boolean} */
goog.userAgent.ASSUME_IE = false;
@deprecated

Marca uma função, método ou propriedade para que usá-lo produza um aviso do compilador indicando que ele não deve mais ser usado.

Exemplo:

/**
 * 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 é usado para criar objetos com um número variável de propriedades. Quando um construtor (Foo no exemplo) é anotado com @dict, só é possível usar a notação de colchete para acessar as propriedades de objetos Foo. A anotação também pode ser usada diretamente em literais de objeto.

Exemplo:

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

Especifica o tipo de uma enumeração. Uma enumeração é um objeto com propriedades que constituem um conjunto de constantes relacionadas. A tag @enum precisa ser seguida por uma expressão de tipo.

O rótulo de tipo de uma enumeração é aplicado a cada propriedade da enumeração. Por exemplo, se uma enumeração tiver o tipo number, cada uma das propriedades enumeradas precisará ser um número. Se o tipo de uma enumeração for omitido, number será usado.

Exemplo:

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

Com este código

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

Quando o compilador é executado com a sinalização --generate_exports, ele gera o código:

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

que exportará os símbolos para o código não compilado. É possível escrever /** @export {SomeType} */ como uma forma abreviada de

/**
 * @export
 * @type {SomeType}
 */

O código que usa a anotação @export precisa:

  1. incluir closure/base.js ou
  2. defina goog.exportSymbol e goog.exportProperty com a mesma assinatura de método na própria base de código.
@extends

Marca uma classe ou interface como herdada de outra classe. Uma classe marcada com @extends também precisa ser marcada com @constructor ou @interface.

Observação: @extends não faz com que uma classe seja herdada de outra classe. A anotação simplesmente diz ao compilador que ele pode tratar uma classe como uma subclasse de outra durante a verificação de tipo.

Para ver um exemplo de implementação da herança, consulte a função goog.inherits() da Biblioteca outro fechamento.

Exemplo:

/**
 * Immutable empty node list.
 * @constructor
 * @extends {goog.ds.BasicNodeList}
 */
goog.ds.EmptyNodeList = function() {
  ...
};
@final

Indica que esta classe não pode ser estendida. Para métodos, indica que nenhuma subclasse tem permissão para substituir esse método.

Exemplo:

/**
 * A class that cannot be extended.
 * @final
 * @constructor
 */
sloth.MyFinalClass = function() { ... }

/**
 * A method that cannot be overridden.
 * @final
 */
sloth.MyFinalClass.prototype.method = function() { ... };
@implements

Usado com @constructor para indicar que uma classe implementa uma interface.

O compilador produz um aviso se você marcar um construtor com @implements e, em seguida, não implementar todos os métodos e propriedades definidos pela interface.

Exemplo:


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

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

Essa anotação só pode aparecer em declarações de propriedade externas. A propriedade tem um tipo declarado, mas é possível atribuir qualquer tipo a ela sem um aviso. Ao acessar a propriedade, você recebe um valor do tipo declarado. Por exemplo, element.innerHTML pode ser atribuído a qualquer tipo, mas sempre retornará uma string.

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

Indica que um método ou propriedade de uma subclasse oculta intencionalmente um método ou propriedade da superclasse e tem exatamente a mesma documentação. Observe que a tag @inheritDoc implanta a tag @override.

Exemplo:

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

Marca uma função como uma interface. Uma interface especifica os membros necessários para um tipo. Qualquer classe que implemente uma interface precisa implementar todos os métodos e propriedades definidos no protótipo da interface. Veja @implements.

O compilador verifica se as interfaces não estão instanciadas. Se a palavra-chave new for usada com uma função de interface, o compilador produzirá um aviso.

Exemplo:

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

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

Indica que as chaves de um literal de objeto precisam ser tratadas como propriedades de algum outro objeto. Essa anotação precisa aparecer apenas em literais de objeto.

Observe que o nome entre chaves não é um nome de tipo, como em outras anotações. É o nome de um objeto. Nomear o objeto a que as propriedades são concedidas. Por exemplo, @type {Foo} significa "uma instância de Foo", mas @lends {Foo} significa "o construtor Foo".

Os documentos do JSDoc Toolkit têm mais informações sobre essa anotação.

Exemplo:

goog.object.extend(
    Button.prototype,
    /** @lends {Button.prototype} */ ({
      isButton: function() { return true; }
    }));
@license ou @preserve

Diz ao compilador para inserir o comentário associado antes do código compilado do arquivo marcado. Essa anotação permite que avisos importantes, como licenças legais ou texto de direitos autorais, sobrevivam à compilação inalterada. As quebras de linha são preservadas.

Exemplo:

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

Indica uma propriedade que não pode ser recolhida pelo compilador em uma variável. O principal uso do @nocollapse é permitir a exportação de propriedades mutáveis. As propriedades não recolhidas ainda podem ser renomeadas pelo compilador. Se você anotar uma propriedade que é um objeto com @nocollapse, todas as propriedades também permanecerão não recolhidas.

Exemplo:

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

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

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

Indica que uma chamada para a função externa declarada não tem efeitos colaterais. Essa anotação permite que o compilador remova chamadas para a função se o valor de retorno não for usado. A anotação só é permitida em extern files.

Exemplo:

/** @nosideeffects */
function noSideEffectsFn1() {}

/** @nosideeffects */
var noSideEffectsFn2 = function() {};

/** @nosideeffects */
a.prototype.noSideEffectsFn3 = function() {};
@override

Indica que um método ou propriedade de uma subclasse oculta intencionalmente um método ou propriedade da superclasse. Se nenhuma outra anotação for incluída, o método ou a propriedade herdará automaticamente as anotações da superclasse dela.

Exemplo:

/**
 * @return {string} Human-readable representation of
 *     project.SubClass.
 * @override
 */
project.SubClass.prototype.toString = function() {
  ...
};
@package

Marca um membro ou uma propriedade como privado. Somente o código no mesmo diretório pode acessar nomes marcados como @package. Especificamente, o código em diretórios pai e filho não pode acessar nomes marcados como @package.

Construtores públicos podem ter propriedades @package para restringir os métodos que os autores de chamada fora do diretório podem usar. Por outro lado, construtores @package podem ter propriedades públicas para impedir que autores de chamadas fora do diretório instanciem diretamente um tipo.

Exemplo:

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

Usado com definições de método, função e construtor para especificar os tipos de argumentos de função. As tags @param precisam estar na mesma ordem que os parâmetros na definição da função.

A tag @param precisa ser seguida por uma expressão de tipo.

Como alternativa, é possível anotar os tipos dos parâmetros inline (consulte a função foo no exemplo).

Exemplo:


/**
 * 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;
}
Para parâmetros que são um padrão de desestruturação, é possível usar qualquer nome que seja um identificador JS válido, após a anotação de tipo.
/**
 * @param {{name: string, age: number}} person
 */
function logPerson({name, age}) {
  console.log(`${name} is ${age} years old`);
}
@private

Marca um membro como particular. Somente o código no mesmo arquivo pode acessar variáveis globais e funções marcadas como @private. Construtores marcados como @private só podem ser instanciados por código no mesmo arquivo e pelos membros estáticos e da instância.

As propriedades estáticas públicas de construtores marcados como @private também podem ser acessadas em qualquer lugar, e o operador instanceof sempre pode acessar membros @private.

Exemplo:

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

@protected

Indica que um membro ou uma propriedade está protegida.

Uma propriedade marcada com @protected pode ser acessada por:

  • todos os códigos no mesmo arquivo
  • métodos estáticos e de instância de qualquer subclasse da classe em que a propriedade está definida.

Exemplo:

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

Marca uma função como uma interface estrutural. Uma interface estrutural é semelhante a uma @interface nominal, mas permite implementações implícitas. Isso significa que qualquer classe que inclua os métodos e propriedades definidos no prototoype da interface estrutural implementa a interface estrutural, usando ou não a tag @implements. Os tipos de registro e os literais de objeto também implementam implicitamente uma interface estrutural se contiverem as propriedades necessárias.

Exemplo:

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

Especifica os tipos de retorno das definições de método e função. A tag @return precisa ser seguida por uma expressão de tipo.

Como alternativa, você pode anotar o tipo de retorno inline. Consulte a função foo no exemplo.

Se uma função que não está em externas não tem valor de retorno, é possível omitir a tag @return, e o compilador vai presumir que a função retorna undefined.

Exemplo:

/**
 * 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 é usado para criar objetos com um número fixo de propriedades. Quando um construtor (Foo no exemplo) é anotado com @struct, só é possível usar a notação de ponto para acessar as propriedades de objetos Foo, não a notação de colchetes. Além disso, não é possível adicionar uma propriedade a uma instância do Foo após a criação dela. A anotação também pode ser usada diretamente em literais de objeto.

Exemplo:

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

Consulte Tipos genéricos.

Exemplo:

/**
 * @param {T} t
 * @constructor
 * @template T
 */
Container = function(t) { ... };
@this

Especifica o tipo do objeto a que a palavra-chave this se refere em uma função. A tag @this precisa ser seguida por uma expressão de tipo.

Para evitar avisos do compilador, é necessário usar uma anotação @this sempre que this aparecer em uma função que não é um método de protótipo nem uma função marcada como @constructor.

Exemplo:

chat.RosterWidget.extern('getRosterElement',
    /**
     * Returns the roster widget element.
     * @this {Widget}
     * @return {Element}
     */
    function() {
      return this.getComponent().getElement();
    });
@throws

Usado para documentar as exceções geradas por uma função. No momento, o verificador de tipos não usa essa informação. Ela só é usada para descobrir se uma função declarada em um arquivo externo tem efeitos colaterais.

Exemplo:

/**
 * @throws {DOMException}
 */
DOMApplicationCache.prototype.swapCache = function() { ... };
@type

Identifica o tipo de uma variável, propriedade ou expressão. A tag @type precisa ser seguida por uma expressão de tipo.

Ao declarar um parâmetro de função ou variável, é possível escrever a anotação de tipo in-line omitindo o {} e o @type, como no segundo exemplo. Esse atalho só pode ser feito em que uma variável ou um parâmetro de função seja declarado. Se você quiser ajustar o tipo mais tarde, precisará de uma transmissão de tipo.

Exemplo:

/**
 * The message hex ID.
 * @type {string}
 */
var hexId = hexId;
var /** string */ name = 'Jamie';
function useSomething(/** (string|number|!Object) */ something) {
...
}
@typedef

Declara um alias para um tipo mais complexo. Atualmente, typedefs só pode ser definido no nível superior, não dentro de funções. Corrigimos essa limitação na nova inferência de tipo.

Exemplo:

/** @typedef {(string|number)} */
goog.NumberLike;

/** @param {goog.NumberLike} x A number or a string. */
goog.readNumber = function(x) {
  ...
}
@unrestricted

Indica que uma classe não é do tipo @struct nem @dict. Esse é o padrão. Geralmente, não é necessário escrever explicitamente, a menos que você esteja usando goog.defineClass ou a palavra-chave class, que produzem classes que são @struct por padrão.

Exemplo:

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

Expressões de tipo

Você pode especificar o tipo de dados de qualquer variável, propriedade, expressão ou parâmetro de função com uma expressão de tipo. Uma expressão de tipo consiste em chaves ("{ }") que contêm alguma combinação dos operadores de tipo descritos abaixo.

Use uma expressão de tipo com a tag @param para declarar o tipo de um parâmetro de função. Use uma expressão de tipo com a tag @type para declarar o tipo de variável, propriedade ou expressão.

Quanto mais tipos você especificar no código, mais otimizações o compilador poderá fazer e mais erros ele poderá detectar.

O compilador usa essas anotações para verificar seu programa. O Frontend Compiler não faz nenhuma promessa de que ele conseguirá descobrir o tipo de cada expressão no seu programa. Essa ferramenta faz um melhor esforço analisando como as variáveis são usadas e nos tipos de anotação anexados às declarações. Em seguida, usa vários algoritmos de inferência de tipo para descobrir o tipo do maior número possível de expressões. Alguns desses algoritmos são diretos ("se x for um número, e y = x; for visto, então y é um número"). Algumas são mais indiretas ("se o primeiro parâmetro de f estiver documentado como uma chamada de retorno que precisa receber um número e virmos f(function(x) { /** ... */ });, então x precisará ser um número").

Nome do operador Exemplos de sintaxe Descrição
Nome do tipo {boolean}
{Window}
{goog.ui.Menu}
Especifica o nome de um tipo.
Tipo de aplicativo {Array<string>}
Uma matriz de strings.

{Object<string, number>}
Um objeto em que as chaves são strings e os valores são números.

Parametriza um tipo com um conjunto de argumentos de tipo. Semelhante a genéricos em Java.
Tipo de união {(number|boolean)}
Um número ou um valor booleano.

Observe os parênteses, que são obrigatórios.
Indica que um valor pode ter o tipo A OU o tipo B.
Tipo de registro {{myNum: number, myObject}}
Um tipo anônimo com uma propriedade chamada myNum, que tem um valor de tipo number, e uma propriedade chamada myObject, que tem um valor de qualquer tipo.

Indica que o valor tem os membros especificados com valores dos tipos especificados.

As chaves são parte da sintaxe do tipo. Por exemplo, para denotar um Array de objetos que têm uma propriedade length, você pode gravar:
Array<{length}>. No exemplo à esquerda, as chaves externas indicam que esse é um tipo de expressão, e as chaves internas indicam que esse é um tipo de registro.

Tipo anulável {?number}
Um número ou null.

Indica que um valor é do tipo A ou null.

Todos os tipos de objeto são anuláveis por padrão, mesmo que não sejam declarados com o operador Nullable. Um tipo de objeto é definido como qualquer coisa, exceto função, string, número ou booleano. Para tornar um tipo de objeto não anulável, use o operador Non-nullable.

Tipo não anulável {!Object}
Um objeto, mas nunca o valor null.

Indica que um valor é do tipo A e não nulo.

As funções e todos os tipos de valor (booleano, número e string) não são anuláveis por padrão, sejam declarados ou não com o operador não anulável. Para tornar um valor ou tipo de função anulável, use o operador Nullable.

Tipo de função {function(string, boolean)}
Uma função que usa dois parâmetros (uma string e um booleano) e tem um valor de retorno desconhecido.
Especifica uma função e os tipos dos parâmetros dela.
Tipo de retorno da função {function(): number}
Uma função que não usa nenhum parâmetro e retorna um número.
Especifica o tipo de valor de retorno de uma função.
Tipo de função this {function(this:goog.ui.Menu, string)}
Uma função que usa um parâmetro (uma string) e é executada no contexto de um goog.ui.Menu.
Especifica o tipo de valor de this na função.
Tipo de função new {function(new:goog.ui.Menu, string)}
Uma função que usa um parâmetro (uma string) e cria uma nova instância de goog.ui.Menu quando chamada com a palavra-chave "novo".
Especifica o tipo construído de um construtor.
Parâmetros variáveis {function(string, ...number): number}
Uma função que usa um parâmetro (uma string) e um número variável de parâmetros que precisam ser números.
Indica que um tipo de função usa um número variável de parâmetros e especifica um tipo para eles.
Parâmetros de variável (em anotações @param) @param {...number} var_args
Um número variável de parâmetros em uma função com anotação.
Indica que a função anotada aceita um número variável de parâmetros e especifica um tipo para eles.
Parâmetro opcional em uma anotação @param @param {number=} opt_argument
Um parâmetro opcional do tipo number.

Indica que o argumento descrito por uma anotação @param é opcional. Uma chamada de função pode omitir um argumento opcional. Um parâmetro opcional não pode preceder um parâmetro não opcional na lista.

Se uma chamada de método omitir um parâmetro opcional, esse argumento terá um valor de undefined. Portanto, se o método armazenar o valor do parâmetro em uma propriedade de classe, a declaração de tipo dessa propriedade precisará incluir um valor possível de undefined, como no exemplo a seguir:

/**
 * 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;
}
Argumento opcional em um tipo de função {function(?string=, number=)}
Uma função que usa uma string opcional e anulável e um número opcional como argumentos.
Indica que um argumento em um tipo de função é opcional. Um argumento opcional pode ser omitido da chamada de função. Um argumento opcional não pode preceder um argumento não opcional na lista de argumentos.
O tipo TODAS {*} Indica que a variável pode assumir qualquer tipo.
O tipo UNKNOWN {?} Indica que a variável pode assumir qualquer tipo de intervalo, e o compilador não pode verificar nenhum uso dela.

Transmissão de tipo

Para transmitir um valor a um tipo específico, use esta sintaxe:

/** @type {!MyType} */ (valueExpression)
Os parênteses ao redor da expressão são sempre obrigatórios.

Tipos genéricos

Assim como o Java, o Frontend Compiler oferece suporte a tipos, funções e métodos genéricos. Os genéricos operam em objetos de vários tipos, preservando a segurança do tipo no momento da compilação.

É possível usar genéricos para implementar coleções generalizadas que contêm referências a objetos de um determinado tipo e algoritmos generalizados que operam em objetos de um determinado tipo.

Declarar um tipo genérico

Um tipo pode ser tornado genérico adicionando uma anotação @template ao construtor do tipo (para classes) ou à declaração da interface (para interfaces). Exemplo:

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

A anotação @template T indica que Foo é um tipo genérico com um tipo de modelo, T. O tipo de modelo T pode ser usado como um tipo no escopo da definição de Foo. Exemplo:

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

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

O método get retornará um objeto do tipo T, e o método set só aceitará objetos do tipo T.

Como instanciar um tipo genérico

Reutilizando o exemplo acima, uma instância de modelo de Foo pode ser criada de várias maneiras:

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

As duas instruções do construtor acima criam uma instância Foo em que o tipo de modelo T é string. O compilador aplicará as chamadas para os métodos de foo e os acessos às propriedades de foo respeitarão o tipo de modelo. Exemplo:

foo.set("hello");  // OK.
foo.set(3);        // Error - expected a string, found a number.
var x = foo.get(); // x is a string.

As instâncias também podem ser implicitamente digitadas por seus argumentos de construtor. Considere um tipo genérico diferente, Bar:

/**
 * @param {T} t
 * @constructor
 * @template T
 */
Bar = function(t) { ... };
var bar = new Bar("hello"); // bar is a Bar<string>

O tipo do argumento para o construtor Bar é inferido como string e, como resultado, a instância criada bar é inferida como Bar<string>.

Vários tipos de modelo

Um genérico pode ter qualquer tipo de modelo. A classe do mapa a seguir tem dois tipos de modelo:

/**
 * @constructor
 * @template Key, Val
 */
MyMap = function() { ... };

Todos os tipos de modelo para um tipo genérico precisam ser especificados na mesma anotação @template, como uma lista separada por vírgulas. A ordem dos nomes de modelo é importante, já que as anotações de tipo modelo vão usar a ordem para parear os tipos de modelo com os valores. Exemplo:

/** @type {MyMap<string, number>} */ var map; // Key = string, Val = number.

Invariação de tipos genéricos

O Frontend Compiler aplica a digitação genérica invariante. Isso significa que, se um contexto espera um tipo Foo<X>, não é possível transmitir um tipo Foo<Y> quando X e Y são tipos diferentes, mesmo que um seja um subtipo do outro. Exemplo:

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

Herança de tipos genéricos

Os tipos genéricos podem ser herdados, e os tipos de modelo deles podem ser fixos ou propagados para o tipo herdeiro. Veja um exemplo de um tipo de herança que corrige o tipo de modelo do supertipo dele:

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

/** @param {T} t */
A.prototype.method = function(t) { ... };

/**
 * @constructor
 * @extends {A<string>}
 */
B = function() { ... };

Ao estender A<string>, o B terá um método method que usa um parâmetro do tipo string.

Veja um exemplo de um tipo herdado propagando o tipo de modelo de seu supertipo:

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

Ao estender A<U>, as instâncias do modelo C terão um método method que usa um parâmetro do tipo de modelo U.

As interfaces podem ser implementadas e estendidas de modo semelhante, mas um único tipo não pode implementar a mesma interface várias vezes com diferentes tipos de modelo. Exemplo:

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

Funções e métodos genéricos

Assim como os tipos genéricos, funções e métodos podem ser tornados genéricos adicionando uma anotação @template à definição. Exemplo:

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