Anotar JavaScript para o Closure 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 Closure Compiler pode usar informações de tipo de dados sobre variáveis JavaScript para fornecer otimização e avisos aprimorados. O JavaScript, no entanto, não tem como declarar tipos.

Como o JavaScript não tem uma sintaxe para declarar o tipo de uma variável, é necessário usar comentários no código para especificar o tipo de dados.

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

  1. Tags JSDoc
  2. Expressões de tipo
  3. Tipos genéricos

Tags JSDoc

O Closure Compiler procura informações de tipo em 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.

Essa tabela inclui apenas tags que afetam o comportamento do Closure Compiler. Para informações sobre outras tags do JSDoc, consulte a documentação do JSDoc Toolkit.

Tag Descrição
@abstract

Marca um método como abstrato. Assim como ao definir um método como goog.abstractMethod, o compilador pode remover métodos anotados com @abstract para reduzir o tamanho do código.

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

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

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

A declaração de tipo é opcional.

O compilador gera 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 vai impedir mudanças nas propriedades dele.

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 no momento da compilação. Com o exemplo à esquerda, é possível transmitir a flag --define='ENABLE_DEBUG=false' ao compilador para mudar 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, um método ou uma propriedade para que o uso dela produza um aviso do compilador indicando que ela não deve mais ser usada.

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

O @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 colchetes 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 cujas propriedades 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 se aplica a cada propriedade dela. 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á presumido.

Exemplo:

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

Dado este código

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

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

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

que vai exportar os símbolos para o código não compilado. Você pode escrever /** @export {SomeType} */ como uma abreviação 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 herdeira 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 herde de outra. A anotação simplesmente informa ao compilador que ele pode tratar uma classe como uma subclasse de outra durante a verificação de tipos.

Para um exemplo de implementação de herança, consulte a função da biblioteca Closure goog.inherits().

Exemplo:

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

Indica que essa classe não pode ser estendida. Para métodos, indica que nenhuma subclasse pode 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 gera um aviso se você marcar um construtor com @implements e 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 propriedades externas. A propriedade tem um tipo declarado, mas você pode atribuir qualquer tipo a ela sem um aviso. Ao acessar a propriedade, você recebe um valor do tipo declarado. Por exemplo, element.innerHTML pode receber qualquer tipo, mas sempre vai 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. A tag @inheritDoc implica a tag @override.

Exemplo:

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

Marca uma função como uma interface. Uma interface especifica os membros obrigatórios de 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 são instanciadas. Se a palavra-chave new for usada com uma função de interface, o compilador vai gerar 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 devem ser tratadas como propriedades de algum outro objeto. Essa anotação só deve aparecer em literais de objeto.

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

A documentação do JSDoc Toolkit tem mais informações sobre essa anotação.

Exemplo:

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

Instrui o compilador a 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 sem alterações. 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 deve ser recolhida pelo compilador em uma variável. O principal uso de @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 dela também vão permanecer 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 vai herdar automaticamente as anotações da superclasse.

Exemplo:

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

Marca um membro ou propriedade como pacote particular. Apenas o código no mesmo diretório pode acessar nomes marcados como @package. Em especial, o código nos diretórios pai e filho não pode acessar nomes marcados como @package.

Os construtores públicos podem ter propriedades @package para restringir os métodos que os chamadores fora do diretório podem usar. Por outro lado, os construtores @package podem ter propriedades públicas para impedir que chamadores 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 dos parâmetros na definição da função.

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

Como alternativa, você pode 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, você pode 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. Apenas o código no mesmo arquivo pode acessar variáveis e funções globais marcadas como @private. Os construtores marcados com @private só podem ser instanciados por código no mesmo arquivo e pelos membros estáticos e de 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 como @protected pode ser acessada por:

  • todo o código no mesmo arquivo
  • métodos estáticos e de instância de qualquer subclasse da classe em que a propriedade é 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 protótipo da interface estrutural implementa a interface estrutural, usando ou não a tag @implements. Tipos de registro e 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 de 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 externs não tiver um valor de retorno, omita a tag @return. 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 Foo depois que ela é criada. 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, use uma anotação @this sempre que this aparecer em uma função que não seja 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 essas informações. Ele só é usado para descobrir se uma função declarada em um arquivo de externs 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 uma variável ou um parâmetro de função, é possível escrever a anotação de tipo inline omitindo {} e @type, como no segundo exemplo. Esse atalho só pode ser usado quando uma variável ou um parâmetro de função é declarado. Se você quiser ajustar o tipo depois, vai precisar de uma conversã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. No momento, os typedefs só podem ser definidos 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 é um tipo @struct nem @dict. Esse é o padrão, então geralmente não é necessário escrever explicitamente, a menos que você esteja usando a palavra-chave class, que produz classes @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

É possível 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 uma 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 o tipo do seu programa. O Closure Compiler não garante que vai conseguir descobrir o tipo de todas as expressões no seu programa. Ele faz o possível para analisar como as variáveis são usadas e as anotações de tipo anexadas às declarações delas. Em seguida, ele 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 simples ("se x for um número e encontrarmos y = x;, então y será um número"). Algumas são mais indiretas ("se o primeiro parâmetro de f for documentado como um callback que precisa receber um número e encontrarmos f(function(x) { /** ... */ });, x precisa 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.
Type Application {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 aos tipos genéricos do Java.
União de tipos {(number|boolean)}
Um número ou um 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 do 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 fazem parte da sintaxe de tipo. Por exemplo, para denotar um Array de objetos que têm uma propriedade length, você pode escrever:
Array<{length}>. No exemplo à esquerda, as chaves externas indicam que é uma expressão de tipo, e as internas indicam que é um tipo de registro.

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

Indica que um valor é do tipo A ou null.

Por padrão, todos os tipos de objeto são anuláveis, sejam ou não declarados com o operador Nullable. Um tipo de objeto é definido como qualquer coisa, exceto uma 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) são não anuláveis por padrão, sejam ou não declarados com o operador "Non-nullable". 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 parâmetros e retorna um número.
Especifica o tipo de um valor de retorno de 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 do 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 "new".
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 os parâmetros variáveis.
Parâmetros de variável (em anotações @param) @param {...number} var_args
Um número variável de parâmetros para uma função anotada.
Indica que a função anotada aceita um número variável de parâmetros e especifica um tipo para os parâmetros variáveis.
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 de parâmetros.

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 anulável opcional 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 ALL {*} Indica que a variável pode assumir qualquer tipo.
O tipo UNKNOWN {?} Indica que a variável pode assumir qualquer tipo e que o compilador não deve verificar o tipo de nenhum uso dela.

Conversão de tipo

Para converter um valor em 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 Closure Compiler é compatível com tipos, funções e métodos genéricos. Os tipos genéricos operam em objetos de vários tipos, preservando a segurança de tipo em tempo de compilação.

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

Declaração de um tipo genérico

Um tipo pode ser genérico adicionando uma anotação @template ao construtor do tipo (para classes) ou à declaração de 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 vai retornar um objeto do tipo T, e o método set só vai aceitar objetos do tipo T.

Instanciar um tipo genérico

Reutilizando o exemplo acima, uma instância com 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 de construtor acima criam uma instância Foo cujo tipo de modelo T é string. O compilador vai forçar que as chamadas aos métodos de foo e os acessos às propriedades de foo respeitem o tipo com 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 digitadas implicitamente pelos argumentos do 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 tipo genérico pode ter qualquer número de tipos de modelo. A classe de 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 tipos de modelo é importante, já que as anotações de tipo com modelo usam a ordem para parear tipos de modelo com os valores. Exemplo:

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

Invariância de tipos genéricos

O Closure Compiler aplica a tipagem 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 de herança. Este é um exemplo de um tipo de herança que corrige o tipo de modelo do supertipo:

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

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

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

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

Confira um exemplo de um tipo de herança que propaga o tipo de modelo do supertipo:

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

Ao estender A<U>, as instâncias com modelo de 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 maneira 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, as funções e os métodos podem ser genéricos adicionando uma anotação @template à definição deles. 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