Anotar JavaScript para Closure Compiler

Nota: Esta página está desactualizada. La lista completa se mantiene en https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler

Descripción general

Closure Compiler puede usar la información de tipo de datos sobre las variables de JavaScript para proporcionar advertencias y optimizaciones mejoradas. Sin embargo, JavaScript no tiene forma de declarar tipos.

Debido a que JavaScript no tiene sintaxis para declarar el tipo de una variable, debes usar comentarios en el código a fin de especificar el tipo de datos.

El lenguaje de tipo de Closure Compiler se deriva de las anotaciones que usa la herramienta de generación de documentos JSDoc, aunque ha cambiado. Ahora incluye varias anotaciones que JSDoc no admite, y viceversa. En este documento, se describe el conjunto de anotaciones y expresiones de tipo que comprende Closure Compiler.

  1. Etiquetas de JSDoc
  2. Expresiones de tipo
  3. Tipos genéricos

Etiquetas de JSDoc

El Closure Compiler busca información de tipo en etiquetas JSDoc. Usa las etiquetas JSDoc que se describen en la tabla de referencia a continuación para ayudar al compilador a optimizar tu código y verificar si es posible encontrar errores de tipo y otros errores.

En esta tabla, se incluyen solo las etiquetas que afectan el comportamiento del Closure Compiler. Para obtener información acerca de otras etiquetas JSDoc, consulta la documentación del kit de herramientas de JSDoc.

Etiqueta Descripción
@abstract

Marca un método como abstracto. De manera similar a la configuración de un método en goog.abstractMethod, el compilador puede reducir los métodos anotados con @abstract para reducir el tamaño de código.

El compilador genera una advertencia si un método marcado con @abstract tiene una implementación no vacía.

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

Marca una variable como de solo lectura. El compilador puede intercalar variables @const, lo que optimiza el código JavaScript.

La declaración de tipo es opcional.

El compilador genera una advertencia si a una variable marcada con @const se le asigna un valor más de una vez. Si la variable es un objeto, ten en cuenta que el compilador no prohíbe los cambios en las propiedades del objeto.

Por ejemplo:
/** @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 una función como constructor. El compilador requiere una anotación @constructor para cualquier función que se use con la palabra clave new.

Por ejemplo:

/**
 * A rectangle.
 * @constructor
 */
function GM_Rect() {
  ...
}
@define Indica una constante que el compilador puede anular en el tiempo de compilación. Con el ejemplo de la izquierda, puedes pasar la marca --define='ENABLE_DEBUG=false' al compilador para cambiar el valor de ENABLE_DEBUG a false. El tipo de una constante definida puede ser un número, una string o un valor booleano. Las definiciones solo se permiten en el alcance global.

Por ejemplo:

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

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

Marca una función, un método o una propiedad de modo que, cuando se use, se genere una advertencia del compilador que indica que ya no debe usarse.

Por ejemplo:

/**
 * 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 se usa para crear objetos con una cantidad variable de propiedades. Cuando un constructor (Foo en el ejemplo) se anota con @dict, solo puedes usar la notación con corchetes para acceder a las propiedades de los objetos Foo. La anotación también se puede usar directamente en los literales de objeto.

Por ejemplo:

/**
 * @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 el tipo de enumeración. Una enumeración es un objeto cuyas propiedades constituyen un conjunto de constantes relacionadas. La etiqueta @enum debe estar seguida por una expresión de tipo.

La etiqueta de tipo de una enumeración se aplica a cada propiedad de la enumeración. Por ejemplo, si una enumeración tiene el tipo number, cada una de sus propiedades enumeradas debe ser un número. Si se omite el tipo de una enumeración, se supone que es number.

Por ejemplo:

/**
 * 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() {
  // ...
};

cuando el compilador se ejecute con la marca --generate_exports, generará el siguiente código:

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

que exportará los símbolos a código sin compilar. Puedes escribir /** @export {SomeType} */ como atajo de

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

El código que usa la anotación @export debe

  1. incluir closure/base.js, o
  2. definen goog.exportSymbol y goog.exportProperty con la misma firma de método en su propia base de código.
@extends

Marca una clase o interfaz como heredada de otra clase. Una clase marcada con @extends también debe estar marcada con @constructor o @interface.

Nota: @extends no hace que una clase herede de otra. La anotación solo le indica al compilador que puede tratar una clase como una subclase de otra durante la verificación de tipo.

Para ver una implementación de herencia de ejemplo, consulta la función de biblioteca de cierre goog.inherits().

Por ejemplo:

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

Indica que no se puede extender esta clase. Para los métodos, indica que ninguna subclase puede anular ese método.

Por ejemplo:

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

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

Se usa con @constructor para indicar que una clase implementa una interfaz.

El compilador genera una advertencia si etiquetas un constructor con @implements y, luego, no implementas todos los métodos y propiedades definidos por la interfaz.

Por ejemplo:


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

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

Esta anotación solo puede aparecer en declaraciones de propiedad de externs. La propiedad tiene un tipo declarado, pero puedes asignarle cualquier tipo sin advertencia. Cuando accedes a la propiedad, obtienes un valor del tipo declarado. Por ejemplo, a element.innerHTML se le puede asignar cualquier tipo, pero siempre mostrará una string.

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

Indica que un método o una propiedad de una subclase oculta intencionalmente un método o una propiedad de la superclase, y tiene exactamente la misma documentación. Ten en cuenta que la etiqueta @inheritDoc implica la etiqueta @override.

Por ejemplo:

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

Marca una función como una interfaz. Una interfaz especifica los miembros necesarios de un tipo. Cualquier clase que implemente una interfaz debe implementar todos los métodos y las propiedades definidas en el prototipo de la interfaz. Consulta @implements.

El compilador verifica que no se creen instancias de interfaces. Si se usa la palabra clave new con una función de la interfaz, el compilador genera una advertencia.

Por ejemplo:

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

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

Indica que las claves de un literal de objeto se deben tratar como propiedades de otro objeto. Esta anotación solo debería aparecer en los literales de objeto.

Ten en cuenta que el nombre entre llaves no es un nombre de tipo como en otras anotaciones. Es un nombre de objeto. Nombra el objeto al que se le prestan las propiedades. Por ejemplo, @type {Foo} significa “una instancia de Foo”, pero @lends {Foo} significa “el constructor Foo”.

En los documentos de JSDoc Toolkit, encontrarás más información sobre esta anotación.

Por ejemplo:

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

Indica al compilador que inserte el comentario asociado antes del código compilado para el archivo marcado. Esta anotación permite que los avisos importantes (como licencias legales o texto de derechos de autor) sobrevivan a la compilación sin cambios. Los saltos de línea se conservan.

Por ejemplo:

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

Denota una propiedad que el compilador no debe contraer en una variable. El uso principal de @nocollapse es permitir la exportación de propiedades mutables. Ten en cuenta que el compilador aún puede cambiar el nombre de las propiedades no contraídas. Si anotas una propiedad que es un objeto con @nocollapse, todas sus propiedades también permanecerán sin contraer.

Por ejemplo:

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

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

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

Indica que una llamada a la función externa declarada no tiene efectos secundarios. Esta anotación permite que el compilador quite llamadas a la función si no se usa el valor que se muestra. La anotación solo se permite en extern files.

Por ejemplo:

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

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

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

Indica que un método o una propiedad de una subclase oculta de manera intencional un método o la propiedad de la superclase. Si no se incluyen otras anotaciones, el método o la propiedad hereda automáticamente las anotaciones de su superclase.

Por ejemplo:

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

Marca un miembro o una propiedad como un paquete privado. Solo el código en el mismo directorio puede acceder a los nombres marcados como @package. En particular, el código en los directorios superiores y secundarios no puede acceder a los nombres marcados como @package.

Los constructores públicos pueden tener propiedades @package para restringir los métodos que pueden usar los emisores fuera del directorio. Por otro lado, los constructores @package pueden tener propiedades públicas para evitar que los emisores que están fuera del directorio creen una instancia directamente.

Por ejemplo:

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

Se usa con definiciones de método, función y constructor para especificar los tipos de argumentos de funciones. Las etiquetas @param deben estar en el mismo orden que los parámetros en la definición de la función.

La etiqueta @param debe estar seguida por una expresión de tipo.

Como alternativa, puedes anotar los tipos de parámetros intercalados (consulta la función foo en el ejemplo).

Por ejemplo:


/**
 * 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 los parámetros que son un patrón de desestructuración, puedes usar cualquier nombre que sea un identificador de JS válido, después de la anotación de tipo.
/**
 * @param {{name: string, age: number}} person
 */
function logPerson({name, age}) {
  console.log(`${name} is ${age} years old`);
}
@private

Marca un miembro como privado. Solo el código del mismo archivo puede acceder a las variables y funciones globales marcadas como @private. Solo se puede crear una instancia de los constructores marcados con @private por código en el mismo archivo y por sus miembros estáticos y de instancia.

También se puede acceder a las propiedades estáticas públicas de los constructores marcados con @private en cualquier lugar, y el operador instanceof siempre puede acceder a los miembros de @private.

Por ejemplo:

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

@protected

Indica que un miembro o una propiedad está protegida.

Una propiedad marcada como @protected es accesible para:

  • todo el código en el mismo archivo
  • métodos de instancia y métodos estáticos de cualquier subclase de la clase en la que la propiedad está definida.

Por ejemplo:

/**
 * 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 una función como una interfaz estructural. Una interfaz estructural es similar a una @interface nominal, pero permite implementaciones implícitas. Esto significa que cualquier clase que incluya los métodos y las propiedades definidos en el prototipo de la interfaz estructural implementa la interfaz estructural, sin importar si usa o no la etiqueta @implements. Los tipos de registro y los literales de objeto también implementan de forma implícita una interfaz estructural si contienen las propiedades requeridas.

Por ejemplo:

/**
 * 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 los tipos de datos que se muestran de las definiciones de métodos y funciones. La etiqueta @return debe estar seguida por una expresión de tipo.

Como alternativa, puedes anotar el tipo de datos que se muestra intercalado (consulta la función foo en el ejemplo).

Si una función que no está en elementos externos no tiene un valor para mostrar, puedes omitir la etiqueta @return y el compilador asumirá que la función muestra undefined.

Por ejemplo:

/**
 * 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 se usa para crear objetos con una cantidad fija de propiedades. Cuando un constructor (Foo en el ejemplo) se anota con @struct, solo puedes usar la notación de puntos para acceder a las propiedades de los objetos Foo, no a la notación con corchetes. Además, no puedes agregar una propiedad a una instancia de Foo después de que se construye. La anotación también se puede usar directamente en los literales de objeto.

Por ejemplo:

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

Consulta Tipos genéricos.

Por ejemplo:

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

Especifica el tipo de objeto al que hace referencia la palabra clave this en una función. La etiqueta @this debe estar seguida por una expresión de tipo.

Para evitar las advertencias del compilador, debes usar una anotación @this cada vez que aparezca this en una función que no es ni un método prototipo ni una función marcada como @constructor.

Por ejemplo:

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

Se usa para documentar las excepciones que arroja una función. El verificador de tipos no usa esta información actualmente. Solo se usa para determinar si una función declarada en un archivo externo tiene efectos secundarios.

Por ejemplo:

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

Identifica el tipo de una variable, una propiedad o una expresión. La etiqueta @type debe estar seguida por una expresión de tipo.

Cuando se declara una variable o un parámetro de función, puedes escribir la anotación de tipo intercalada y omitir {} y @type, como en el segundo ejemplo. Esta combinación de teclas solo se puede realizar cuando se declara un parámetro de función o variable. Si quieres ajustar el tipo más adelante, necesitarás una conversión de tipo.

Por ejemplo:

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

Declara un alias para un tipo más complejo. Actualmente, typedefs solo se puede definir en el nivel superior, no dentro de funciones. Corregimos esta limitación en la inferencia de tipo nueva.

Por ejemplo:

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

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

Indica que una clase no es un tipo @struct ni un tipo @dict. Esta es la opción predeterminada, por lo que, por lo general, no es necesario escribirla de forma explícita, a menos que uses goog.defineClass o la palabra clave class, que producen clases que son @struct de forma predeterminada.

Por ejemplo:

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

Expresiones de tipo

Puedes especificar el tipo de datos de cualquier variable, propiedad, expresión o parámetro de función con una expresión de tipo. Una expresión de tipo consta de llaves (“{ }") que contienen alguna combinación de los operadores de tipo descritos a continuación.

Usa una expresión de tipo con la etiqueta @param para declarar el tipo de un parámetro de función. Usa una expresión de tipo con la etiqueta @type para declarar el tipo de una variable, una propiedad o una expresión.

Cuantos más tipos especifiques en tu código, más optimizaciones podrá realizar el compilador y más errores podrá detectar.

El compilador usa estas anotaciones para comprobar el tipo de programa. Ten en cuenta que Closure Compiler no realiza ninguna promesa de que podrá determinar el tipo de cada expresión de tu programa. Hace su mejor esfuerzo observando cómo se usan las variables y las anotaciones de tipo adjuntas a sus declaraciones. Luego, utiliza varios algoritmos de inferencia de tipo para determinar el tipo de la mayor cantidad posible de expresiones. Algunos de estos algoritmos son directos ("si x es un número y vemos y = x;, entonces y es un número"). Algunos son más indirectos ("si el primer parámetro de f está documentado como una devolución de llamada que debe tomar un número, y vemos f(function(x) { /** ... */ });, entonces x debe ser un número").

Nombre de operador Ejemplos de sintaxis Descripción
Nombre del tipo {boolean}
{Window}
{goog.ui.Menu}
Especifica el nombre de un tipo.
Tipo de aplicación {Array<string>}
Un arreglo de strings.

{Object<string, number>}
Es un objeto en el que las claves son strings y los valores son números.

Parametriza un tipo con un conjunto de argumentos de tipo. Similar a los genéricos de Java.
Tipo: unión {(number|boolean)}
Es un número o un valor booleano.

Ten en cuenta los paréntesis, que son obligatorios.
Indica que un valor puede tener el tipo A O tipo B.
Tipo de registro {{myNum: number, myObject}}
Es un tipo anónimo con una propiedad llamada myNum que tiene un valor de tipo number y una propiedad llamada myObject que tiene un valor de cualquier tipo.

Indica que el valor tiene los miembros especificados con valores de los tipos especificados.

Las llaves forman parte de la sintaxis de tipo. Por ejemplo, para denotar un Array de objetos que tienen una propiedad length, puedes escribir:
Array<{length}>. En el ejemplo de la izquierda, las llaves externas indican que esta es una expresión de tipo y las llaves internas indican que este es un tipo de registro.

Tipo anulable {?number}
Un número o null.

Indica que un valor es de tipo A o null.

Todos los tipos de objetos son anulables de forma predeterminada, ya sea que estén declarados con el operador Null o no. Un tipo de objeto se define como cualquier cosa, excepto una función, una string, un número o un valor booleano. Para hacer que un tipo de objeto sea no anulable, usa el operador No anulable.

Tipo no anulable {!Object}
Es un objeto, pero nunca el valor null.

Indica que un valor es de tipo A y no es nulo.

Las funciones y todos los tipos de valores (booleano, número y string) no son anulables de forma predeterminada, ya sea que estén declarados con el operador no anulable o no. Para hacer que un valor o un tipo de función sea anulable, usa el operador Nullable.

Tipo de función {function(string, boolean)}
Una función que toma dos parámetros (una string y un valor booleano) y tiene un valor de retorno desconocido.
Especifica una función y los tipos de parámetros de la función.
Tipo de datos que se muestra de la función {function(): number}
Es una función que no toma parámetros y muestra un número.
Especifica el tipo de valor que se muestra de una función.
Tipo de función this {function(this:goog.ui.Menu, string)}
Es una función que toma un parámetro (una string) y se ejecuta en el contexto de un goog.ui.
Especifica el tipo de valor de this dentro de la función.
Tipo de función new {function(new:goog.ui.Menu, string)}
Es una función que toma un parámetro (una string) y crea una instancia nueva de goog.ui.Menu cuando se llama con la palabra clave "new".
Especifica el tipo construido de un constructor.
Parámetros variables {function(string, ...number): number}
Una función que toma un parámetro (una string) y, luego, una cantidad variable de parámetros que deben ser números.
Indica que un tipo de función toma una cantidad variable de parámetros y especifica un tipo de parámetros.
Parámetros variables (en anotaciones @param) @param {...number} var_args
Cantidad variable de parámetros para una función anotada.
Indica que la función anotada acepta una cantidad variable de parámetros y especifica un tipo para los parámetros variables.
Parámetro opcional en una anotación @param @param {number=} opt_argument
Un parámetro opcional del tipo number.

Indica que el argumento descrito por una anotación @param es opcional. Una llamada a función puede omitir un argumento opcional. Un parámetro opcional no puede preceder a un parámetro no opcional en la lista de parámetros.

Si una llamada de método omite un parámetro opcional, ese argumento tendrá un valor de undefined. Por lo tanto, si el método almacena el valor del parámetro en una propiedad de clase, la declaración de tipo de esa propiedad debe incluir un valor posible de undefined, como en el siguiente ejemplo:

/**
 * 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 en un tipo de función {function(?string=, number=)}
Una función que toma una string opcional anulable y un número opcional como argumentos.
Indica que un argumento en un tipo de función es opcional. Se puede omitir un argumento opcional de la llamada a función. Un argumento opcional no puede preceder a un argumento no opcional en la lista de argumentos.
El tipo TODO {*} Indica que la variable puede adoptar cualquier tipo.
El tipo UNKNOWN {?} Indica que la variable puede tomar cualquier tipo, y el compilador no debe verificar ningún uso de la misma.

Conversión de tipos

Para convertir un valor en un tipo específico, usa esta sintaxis

/** @type {!MyType} */ (valueExpression)
Los paréntesis alrededor de la expresión siempre son obligatorios.

Tipos genéricos

Al igual que Java, el compilador de Closure admite tipos, funciones y métodos genéricos. Los genéricos operan en objetos de varios tipos y conservan la seguridad de tipo en tiempo de compilación.

Puedes usar elementos genéricos para implementar colecciones generalizadas que tengan referencias a objetos de un tipo determinado y algoritmos generalizados que operen sobre objetos de un tipo determinado.

Declaración de un tipo genérico

Un tipo se puede hacer genérico agregando una anotación @template al constructor del tipo (para clases) o la declaración de la interfaz (para las interfaces). Por ejemplo:

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

La anotación @template T indica que Foo es un tipo genérico con un tipo de plantilla, T. El tipo de plantilla T se puede usar como un tipo dentro del alcance de la definición de Foo. Por ejemplo:

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

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

El método get mostrará un objeto de tipo T, y el método set solo aceptará objetos de tipo T.

Crea una instancia de un tipo genérico

Si reutilizas el ejemplo anterior, se puede crear una instancia con plantilla de Foo de varias maneras:

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

Las dos instrucciones de constructor anteriores crean una instancia Foo cuyo tipo de plantilla T es string. El compilador aplicará esa llamada a los métodos de foo y accederá a las propiedades de foo, respetará el tipo de plantilla. Por ejemplo:

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

Las instancias también se pueden escribir de forma implícita con sus argumentos de constructor. Considera un tipo genérico diferente, Bar:

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

El tipo del argumento para el constructor Bar se infiere como string y, como resultado, la instancia creada bar se infiere como Bar<string>.

Varios tipos de plantillas

Un genérico puede tener cualquier cantidad de tipos de plantillas. La siguiente clase de mapa tiene dos tipos de plantillas:

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

Todos los tipos de plantilla para un tipo genérico deben especificarse en la misma anotación @template, como una lista separada por comas. El orden de los nombres de los tipos de plantilla es importante, ya que las anotaciones de tipo de plantilla usarán el orden para sincronizar los tipos de plantilla con los valores. Por ejemplo:

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

Invariancia de tipos genéricos

El Closure Compiler aplica la escritura genérica invariable. Esto significa que si un contexto espera un tipo Foo<X>, no puedes pasar un tipo Foo<Y> cuando X y Y son tipos diferentes, incluso si uno es un subtipo del otro. Por ejemplo:

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

Herencia de tipos genéricos

Los tipos genéricos se pueden heredar y sus tipos de plantillas se pueden corregir o propagar al tipo heredado. A continuación, se muestra un ejemplo de un tipo heredado que corrige el tipo de plantilla de su supertipo:

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

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

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

Si extiendes A<string>, B tendrá un método method que tome un parámetro de tipo string.

Este es un ejemplo de un tipo heredado que propaga el tipo de plantilla de su supertipo:

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

Si extiendes A<U>, las instancias con plantilla de C tendrán un método method que tome un parámetro del tipo de plantilla U.

Las interfaces se pueden implementar y extender de manera similar, pero un solo tipo no puede implementar la misma interfaz varias veces con diferentes tipos de plantillas. Por ejemplo:

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

Funciones y métodos genéricos

Al igual que los tipos genéricos, las funciones y los métodos pueden hacerse genéricos si agregas una anotación @template a su definición. Por ejemplo:

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