Anotación de JavaScript para el compilador de Closure

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

El compilador de cierre puede usar información sobre el tipo de datos de las variables de JavaScript para proporcionar optimizaciones y advertencias mejoradas. Sin embargo, JavaScript no tiene forma de declarar tipos.

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

El lenguaje de tipos del compilador de Closure se deriva de las anotaciones que usa la herramienta de generación de documentos JSDoc, aunque desde entonces se ha diferenciado. 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 el compilador de Closure.

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

Etiquetas de JSDoc

El compilador de Closure busca información de tipos en las etiquetas de JSDoc. Usa las etiquetas JSDoc que se describen en la siguiente tabla de referencia para ayudar al compilador a optimizar tu código y verificar si hay posibles errores de tipo y otros errores.

Esta tabla solo incluye las etiquetas que afectan el comportamiento del compilador de cierre. Para obtener información sobre otras etiquetas de JSDoc, consulta la documentación del kit de herramientas de JSDoc.

Etiqueta Descripción
@abstract

Marca un método como abstracto. Al igual que cuando se configura un método como goog.abstractMethod, el compilador puede quitar los métodos anotados con @abstract para reducir el tamaño del 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 insertar variables @const de forma intercalada, 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 momento de la 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 cadena 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 para que su uso genere una advertencia del compilador que indique que ya no se debe usar.

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 de corchetes para acceder a las propiedades de los objetos Foo. La anotación también se puede usar directamente en literales de objetos.

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. Un enum es un objeto cuyas propiedades constituyen un conjunto de constantes relacionadas. La etiqueta @enum debe ir seguida de una expresión de tipo.

La etiqueta de tipo de un enum se aplica a cada propiedad del enum. Por ejemplo, si un enum 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 se ejecuta el compilador con la marca --generate_exports, se genera el siguiente código:

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

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

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

El código que usa la anotación @export debe cumplir con uno de los siguientes requisitos:

  1. Incluye closure/base.js.
  2. define 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 simplemente le indica al compilador que puede tratar una clase como una subclase de otra durante la verificación de tipos.

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

Por ejemplo:

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

Indica que no se permite extender esta clase. En el caso de 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 propiedades externas. La propiedad tiene un tipo declarado, pero puedes asignarle cualquier tipo sin una 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 devolverá una cadena.

/**
 * @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 requeridos de un tipo. Cualquier clase que implemente una interfaz debe implementar todos los métodos y las propiedades definidos en el prototipo de la interfaz. Consulta @implements.

El compilador verifica que no se creen instancias de las interfaces. Si la palabra clave new se usa con una función de 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 algún otro objeto. Esta anotación solo debe aparecer en literales de objetos.

Observa 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 prestan las propiedades. Por ejemplo, @type {Foo} significa "una instancia de Foo", pero @lends {Foo} significa "el constructor Foo".

En la documentación del kit de herramientas de JSDoc, 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 las licencias legales o el texto de derechos de autor) sobrevivan a la compilación sin cambios. Se conservan los saltos de línea.

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 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 las llamadas a la función si no se usa el valor de devolución. 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 intencionalmente un método o una propiedad de la superclase. Si no se incluyen otras anotaciones, el método o la propiedad heredan 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 privados para el paquete. Solo el código del mismo directorio puede acceder a los nombres marcados como @package. En particular, el código de los directorios principal y secundario 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 llamadores fuera del directorio. Por otro lado, los constructores de @package pueden tener propiedades públicas para evitar que los llamadores fuera del directorio creen instancias de un tipo 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étodos, funciones y constructores 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 ir seguida de una expresión de tipo.

Como alternativa, puedes anotar los tipos de los parámetros de forma intercalada (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 a un miembro como privado. Solo el código del mismo archivo puede acceder a las variables y funciones globales marcadas como @private. Los constructores marcados como @private solo pueden instanciarse con código en el mismo archivo y con sus miembros estáticos y de instancia.

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

Por ejemplo:

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

Indica que un miembro o una propiedad están protegidos.

Se puede acceder a una propiedad marcada como @protected de las siguientes maneras:

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

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, ya sea que use o no la etiqueta @implements. Los tipos de registros y los literales de objetos 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 devolución de las definiciones de métodos y funciones. La etiqueta @return debe ir seguida de una expresión de tipo.

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

Si una función que no está en externs no tiene un valor de devolución, puedes omitir la etiqueta @return, y el compilador supondrá que la función devuelve 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 la notación de 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 literales de objetos.

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 del objeto al que hace referencia la palabra clave this dentro de una función. La etiqueta @this debe ir seguida de una expresión de tipo.

Para evitar advertencias del compilador, debes usar una anotación @this siempre que this aparezca en una función que no sea un método de 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. Actualmente, el verificador de tipos no usa esta información. Solo se usa para determinar si una función declarada en un archivo de externs tiene efectos secundarios.

Por ejemplo:

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

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

Cuando declaras un parámetro de función o variable, puedes escribir la anotación de tipo intercalada omitiendo {} y @type, como en el segundo ejemplo. Este atajo solo se puede realizar donde se declara un parámetro de variable o función. 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, los typedefs solo se pueden definir en el nivel superior, no dentro de las funciones. Corregimos esta limitación en la nueva inferencia de tipos.

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 de tipo @struct ni de tipo @dict. Este es el valor predeterminado, por lo que, en general, no es necesario escribirlo de forma explícita, a menos que uses la palabra clave class, que produce clases que son @structs 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 tipos

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 que se describen 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 verificar el tipo de tu programa. Ten en cuenta que el compilador de Closure no garantiza que podrá determinar el tipo de cada expresión de tu programa. Hace su mejor esfuerzo analizando cómo se usan las variables y las anotaciones de tipo adjuntas a sus declaraciones. Luego, usa varios algoritmos de inferencia de tipos para determinar el tipo de la mayor cantidad posible de expresiones. Algunos de estos algoritmos son sencillos ("si x es un número y vemos y = x;, entonces y es un número"). Algunas son más indirectas ("si el primer parámetro de f se documenta 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.
Escribe la aplicación {Array<string>}
Un array de cadenas.

{Object<string, number>}
Objeto en el que las claves son cadenas y los valores son números.

Parametriza un tipo con un conjunto de argumentos de tipo. Es similar a los genéricos de Java.
Unión de tipos {(number|boolean)}
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 el tipo B.
Tipo de registro {{myNum: number, myObject}}
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.

Los corchetes forman parte de la sintaxis de tipos. Por ejemplo, para denotar un Array de objetos que tienen una propiedad length, puedes escribir:
Array<{length}>. En el ejemplo de la izquierda, los corchetes externos indican que se trata de una expresión de tipo, y los corchetes internos indican que se trata de 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 admiten valores nulos de forma predeterminada, independientemente de si se declaran con el operador Nullable. Un tipo de objeto se define como cualquier elemento, excepto una función, una cadena, un número o un valor booleano. Para hacer que un tipo de objeto no sea anulable, usa el operador Non-nullable.

Tipo no anulable {!Object}
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 cadena) no son anulables de forma predeterminada, independientemente de si se declaran con el operador Non-nullable. Para hacer que un valor o un tipo de función sean anulables, usa el operador Nullable.

Tipo de función {function(string, boolean)}
Una función que toma dos parámetros (una cadena y un valor booleano) y tiene un valor de retorno desconocido.
Especifica una función y los tipos de los parámetros de la función.
Tipo de datos que devuelve la función {function(): number}
Una función que no toma parámetros y devuelve un número.
Especifica el tipo de valor que devuelve una función.
Tipo de función this {function(this:goog.ui.Menu, string)}
Una función que toma un parámetro (una cadena) y se ejecuta en el contexto de un goog.ui.Menu.
Especifica el tipo del valor de this dentro de la función.
Tipo de función new {function(new:goog.ui.Menu, string)}
Una función que toma un parámetro (una cadena) 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 cadena) 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 para los parámetros variables.
Parámetros de variables (en anotaciones de @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
Es un parámetro opcional de tipo number.

Indica que el argumento descrito por una anotación @param es opcional. Una llamada a una 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 a un 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 cadena opcional y anulable y un número opcional como argumentos.
Indica que un argumento en un tipo de función es opcional. Un argumento opcional se puede omitir en la llamada a la función. Un argumento opcional no puede preceder a un argumento no opcional en la lista de argumentos.
El tipo ALL {*} Indica que la variable puede adoptar cualquier tipo.
El tipo UNKNOWN {?} Indica que la variable puede adoptar cualquier tipo y que el compilador no debe verificar el tipo de ninguno de sus usos.

Conversión de tipos

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

/** @type {!MyType} */ (valueExpression)
Siempre se requieren los paréntesis alrededor de la expresión.

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 tipos en tiempo de compilación.

Puedes usar los genéricos para implementar colecciones generalizadas que contengan referencias a objetos de un tipo en particular y algoritmos generalizados que operen sobre objetos de un tipo en particular.

Cómo declarar un tipo genérico

Un tipo puede volverse genérico si se agrega una anotación @template al constructor del tipo (para clases) o a la declaración de la interfaz (para 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 devolverá un objeto de tipo T, y el método set solo aceptará objetos de tipo T.

Cómo crear una instancia de un tipo genérico

Si se reutiliza el ejemplo anterior, se puede crear una instancia basada en plantillas de Foo de varias maneras:

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

Ambas instrucciones del constructor anteriores crean una instancia de Foo cuyo tipo de plantilla T es string. El compilador aplicará que las llamadas a los métodos de foo y los accesos a las propiedades de foo respeten 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 pueden tener un tipo implícito por 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 basadas en plantillas usarán el orden para asociar los tipos de plantilla con los valores. Por ejemplo:

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

Invariancia de los tipos genéricos

El compilador de Closure aplica la escritura de tipos genéricos invariantes. 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 plantilla pueden ser fijos o propagarse al tipo que hereda. 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 toma 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() { ... };

Cuando se extiende A<U>, las instancias basadas en plantillas de C tendrán un método method que toma 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 se pueden hacer genéricos agregando 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