Annoter JavaScript pour le compilateur Closure

Remarque : Cette page est obsolète. La liste complète est disponible sur https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler .

Présentation

Le compilateur Closure peut utiliser des informations sur les types de données des variables JavaScript pour fournir une optimisation et des avertissements améliorés. JavaScript, en revanche, ne permet pas de déclarer des types.

Comme JavaScript ne dispose d'aucune syntaxe pour déclarer le type d'une variable, vous devez utiliser des commentaires dans le code pour spécifier le type de données.

Le langage de typographie du compilateur Closure est dérivé des annotations utilisées par l'outil de génération de documents JSDoc, bien qu'il ait évolué depuis. Il inclut désormais plusieurs annotations que JSDoc ne prend pas en charge, et vice versa. Ce document décrit l'ensemble des annotations et des expressions de type que comprend Closure Compiler.

  1. Balises JSDoc
  2. Expressions de type
  3. Types génériques

Balises JSDoc

Le compilateur Closure recherche des informations sur les types dans les balises JSDoc. Utilisez les balises JSDoc décrites dans le tableau de référence ci-dessous pour aider le compilateur à optimiser votre code et à vérifier s'il contient d'éventuelles erreurs de type et autres erreurs.

Ce tableau n'inclut que les tags qui affectent le comportement du compilateur Closure. Pour en savoir plus sur les autres tags JSDoc, consultez la documentation JSDoc Toolkit.

Tag Description
@abstract

Marque une méthode comme abstraite. Comme pour la définition d'une méthode sur goog.abstractMethod, le compilateur peut supprimer les méthodes annotées avec @abstract pour réduire la taille du code.

Le compilateur génère un avertissement si une méthode marquée avec @abstract a une implémentation non vide.

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

Marque une variable comme étant en lecture seule. Le compilateur peut insérer les variables @const, ce qui optimise le code JavaScript.

La déclaration de type est facultative.

Le compilateur génère un avertissement si une variable marquée avec @const se voit attribuer une valeur plusieurs fois. Si la variable est un objet, notez que le compilateur n'interdit pas les modifications apportées aux propriétés de l'objet.

Exemple :
/** @const */ var MY_BEER = 'stout';

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

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

Marque une fonction comme constructeur. Le compilateur exige une annotation @constructor pour toute fonction utilisée avec le mot clé new.

Exemple :

/**
 * A rectangle.
 * @constructor
 */
function GM_Rect() {
  ...
}
@define Indique une constante qui peut être remplacée par le compilateur au moment de la compilation. Dans l'exemple de gauche, vous pouvez transmettre l'indicateur --define='ENABLE_DEBUG=false' au compilateur pour remplacer la valeur de ENABLE_DEBUG par false. Le type d'une constante définie peut être un nombre, une chaîne ou une valeur booléenne. Les définitions ne sont autorisées que dans le champ d'application global.

Exemple :

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

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

Marque une fonction, une méthode ou une propriété de sorte que son utilisation génère un avertissement du compilateur indiquant qu'elle ne doit plus être utilisée.

Exemple :

/**
 * 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 est utilisé pour créer des objets avec un nombre variable de propriétés. Lorsqu'un constructeur (Foo dans l'exemple) est annoté avec @dict, vous ne pouvez utiliser la notation entre crochets que pour accéder aux propriétés des objets Foo. L'annotation peut également être utilisée directement sur les littéraux d'objet.

Exemple :

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

Spécifie le type d'une énumération. Une énumération est un objet dont les propriétés constituent un ensemble de constantes associées. La balise @enum doit être suivie d'une expression de type.

Le libellé de type d'une énumération s'applique à chaque propriété de l'énumération. Par exemple, si une énumération est de type number, chacune de ses propriétés énumérées doit être un nombre. Si le type d'un enum est omis, number est utilisé.

Exemple :

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

Prenons l'exemple de code suivant :

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

Lorsque le compilateur est exécuté avec l'indicateur --generate_exports, il génère le code suivant :

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

qui exportera les symboles vers le code non compilé. Vous pouvez écrire /** @export {SomeType} */ comme raccourci pour

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

Le code qui utilise l'annotation @export doit

  1. inclure closure/base.js, ou
  2. définir à la fois goog.exportSymbol et goog.exportProperty avec la même signature de méthode dans leur propre codebase.
@extends

Marque une classe ou une interface comme héritant d'une autre classe. Une classe marquée avec @extends doit également être marquée avec @constructor ou @interface.

Remarque : @extends n'entraîne pas l'héritage d'une classe à partir d'une autre classe. L'annotation indique simplement au compilateur qu'il peut traiter une classe comme une sous-classe d'une autre lors de la vérification du type.

Pour obtenir un exemple d'implémentation de l'héritage, consultez la fonction goog.inherits() de la bibliothèque Closure.

Exemple :

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

Indique que cette classe ne peut pas être étendue. Pour les méthodes, indique qu'aucune sous-classe n'est autorisée à remplacer cette méthode.

Exemple :

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

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

Utilisé avec @constructor pour indiquer qu'une classe implémente une interface.

Le compilateur génère un avertissement si vous marquez un constructeur avec @implements et que vous n'implémentez pas toutes les méthodes et propriétés définies par l'interface.

Exemple :

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

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

Cette annotation ne peut apparaître que dans les déclarations de propriétés externes. La propriété a un type déclaré, mais vous pouvez lui attribuer n'importe quel type sans avertissement. Lorsque vous accédez à la propriété, vous obtenez une valeur du type déclaré. Par exemple, element.innerHTML peut être associé à n'importe quel type, mais renverra toujours une chaîne.

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

Indique qu'une méthode ou une propriété d'une sous-classe masque intentionnellement une méthode ou une propriété de la super-classe et possède exactement la même documentation. Notez que la balise @inheritDoc implique la balise @override.

Exemple :

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

Marque une fonction comme interface. Une interface spécifie les membres requis d'un type. Toute classe qui implémente une interface doit implémenter toutes les méthodes et propriétés définies sur le prototype de l'interface. Voir @implements.

Le compilateur vérifie que les interfaces ne sont pas instanciées. Si le mot clé new est utilisé avec une fonction d'interface, le compilateur génère un avertissement.

Exemple :

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

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

Indique que les clés d'un littéral d'objet doivent être traitées comme des propriétés d'un autre objet. Cette annotation ne doit apparaître que sur les littéraux d'objet.

Notez que le nom entre accolades n'est pas un nom de type comme dans d'autres annotations. Il s'agit d'un nom d'objet. Il nomme l'objet auquel les propriétés sont prêtées. Par exemple, @type {Foo} signifie "une instance de Foo", mais @lends {Foo} signifie "le constructeur Foo".

La documentation de JSDoc Toolkit fournit plus d'informations sur cette annotation.

Exemple :

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

Indique au compilateur d'insérer le commentaire associé avant le code compilé pour le fichier marqué. Cette annotation permet aux avis importants (tels que les licences légales ou le texte sur les droits d'auteur) de survivre à la compilation sans être modifiés. Les sauts de ligne sont conservés.

Exemple :

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

Désigne une propriété qui ne doit pas être réduite par le compilateur en une variable. L'utilisation principale de @nocollapse est de permettre l'exportation de propriétés mutables. Notez que les propriétés non réduites peuvent toujours être renommées par le compilateur. Si vous annotez une propriété qui est un objet avec @nocollapse, toutes ses propriétés resteront également non réduites.

Exemple :

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

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

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

Indique qu'un appel à la fonction externe déclarée n'a aucun effet secondaire. Cette annotation permet au compilateur de supprimer les appels à la fonction si la valeur renvoyée n'est pas utilisée. L'annotation n'est autorisée que dans extern files.

Exemple :

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

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

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

Indique qu'une méthode ou une propriété d'une sous-classe masque intentionnellement une méthode ou une propriété de la super-classe. Si aucune autre annotation n'est incluse, la méthode ou la propriété hérite automatiquement des annotations de sa superclasse.

Exemple :

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

Marque un membre ou une propriété comme étant privés au niveau du package. Seul le code du même répertoire peut accéder aux noms marqués @package. En particulier, le code des répertoires parent et enfant ne peut pas accéder aux noms marqués @package.

Les constructeurs publics peuvent avoir des propriétés @package pour restreindre les méthodes que les appelants en dehors du répertoire peuvent utiliser. En revanche, les constructeurs @package peuvent avoir des propriétés publiques pour empêcher les appelants en dehors du répertoire d'instancier directement un type.

Exemple :

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

Utilisé avec les définitions de méthodes, de fonctions et de constructeurs pour spécifier les types d'arguments de fonction. Les balises @param doivent être dans le même ordre que les paramètres de la définition de la fonction.

La balise @param doit être suivie d'une expression de type.

Vous pouvez également annoter les types de paramètres en ligne (voir la fonction foo dans l'exemple).

Exemple :

/**
 * 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;
}
Pour les paramètres qui sont un modèle de déstructuration, vous pouvez utiliser n'importe quel nom qui est un identifiant JS valide, après l'annotation de type.
/**
 * @param {{name: string, age: number}} person
 */
function logPerson({name, age}) {
  console.log(`${name} is ${age} years old`);
}
@private

Marque un membre comme privé. Seul le code du même fichier peut accéder aux variables et fonctions globales marquées @private. Les constructeurs marqués @private ne peuvent être instanciés que par le code du même fichier et par leurs membres statiques et d'instance.

Les propriétés statiques publiques des constructeurs marqués @private sont également accessibles partout, et l'opérateur instanceof peut toujours accéder aux membres @private.

Exemple :

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

Indique qu'un membre ou une propriété sont protégés.

Une propriété marquée @protected est accessible aux utilisateurs suivants :

  • tout le code dans le même fichier.
  • méthodes statiques et méthodes d'instance de toute sous-classe de la classe sur laquelle la propriété est définie.

Exemple :

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

Marque une fonction comme interface structurelle. Une interface structurelle est semblable à une interface nominale @interface, mais autorise les implémentations implicites. Cela signifie que toute classe qui inclut les méthodes et les propriétés définies sur le prototype de l'interface structurelle implémente l'interface structurelle, qu'elle utilise ou non la balise @implements. Les types d'enregistrements et les littéraux d'objets implémentent également implicitement une interface structurelle s'ils contiennent les propriétés requises.

Exemple :

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

Spécifie les types de retour des définitions de méthodes et de fonctions. La balise @return doit être suivie d'une expression de type.

Vous pouvez également annoter le type renvoyé de manière intégrée (voir la fonction foo dans l'exemple).

Si une fonction qui ne se trouve pas dans les externs n'a pas de valeur de retour, vous pouvez omettre la balise @return. Le compilateur supposera alors que la fonction renvoie undefined.

Exemple :

/**
 * 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 permet de créer des objets avec un nombre fixe de propriétés. Lorsqu'un constructeur (Foo dans l'exemple) est annoté avec @struct, vous ne pouvez utiliser que la notation par points pour accéder aux propriétés des objets Foo, et non la notation entre crochets. De plus, vous ne pouvez pas ajouter de propriété à une instance Foo après sa construction. L'annotation peut également être utilisée directement sur les littéraux d'objet.

Exemple :

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

Consultez Types génériques.

Exemple :

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

Spécifie le type d'objet auquel le mot clé this fait référence dans une fonction. La balise @this doit être suivie d'une expression de type.

Pour éviter les avertissements du compilateur, vous devez utiliser une annotation @this chaque fois que this apparaît dans une fonction qui n'est ni une méthode de prototype ni une fonction marquée comme @constructor.

Exemple :

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

Utilisé pour documenter les exceptions générées par une fonction. Le vérificateur de type n'utilise pas ces informations pour le moment. Il n'est utilisé que pour déterminer si une fonction déclarée dans un fichier externs a des effets secondaires.

Exemple :

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

Identifie le type d'une variable, d'une propriété ou d'une expression. La balise @type doit être suivie d'une expression de type.

Lorsque vous déclarez un paramètre de variable ou de fonction, vous pouvez écrire l'annotation de type en ligne en omettant {} et @type, comme dans le deuxième exemple. Ce raccourci ne peut être utilisé que lorsqu'un paramètre de variable ou de fonction est déclaré. Si vous souhaitez ajuster le type ultérieurement, vous aurez besoin d'un cast de type.

Exemple :

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

Déclare un alias pour un type plus complexe. Actuellement, les typedefs ne peuvent être définis qu'au niveau supérieur, et non à l'intérieur des fonctions. Nous avons corrigé cette limitation dans la nouvelle inférence de type.

Exemple :

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

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

Indique qu'une classe n'est ni de type @struct ni de type @dict. Il s'agit de la valeur par défaut. Il n'est donc généralement pas nécessaire de l'écrire explicitement, sauf si vous utilisez le mot clé class, qui produit des classes @struct par défaut.

Exemple :

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

Expressions de type

Vous pouvez spécifier le type de données de n'importe quelle variable, propriété, expression ou paramètre de fonction avec une expression de type. Une expression de type se compose d'accolades ("{ }") contenant une combinaison des opérateurs de type décrits ci-dessous.

Utilisez une expression de type avec la balise @param pour déclarer le type d'un paramètre de fonction. Utilisez une expression de type avec le tag @type pour déclarer le type d'une variable, d'une propriété ou d'une expression.

Plus vous spécifiez de types dans votre code, plus le compilateur peut effectuer d'optimisations et détecter d'erreurs.

Le compilateur utilise ces annotations pour vérifier le type de votre programme. Notez que Closure Compiler ne garantit pas qu'il sera en mesure de déterminer le type de chaque expression de votre programme. Il fait de son mieux en examinant la façon dont les variables sont utilisées et les annotations de type associées à leurs déclarations. Il utilise ensuite un certain nombre d'algorithmes d'inférence de type pour déterminer le type du plus grand nombre possible d'expressions. Certains de ces algorithmes sont simples ("si x est un nombre et que nous voyons y = x;, alors y est un nombre"). Certaines sont plus indirectes ("si le premier paramètre de f est documenté comme un rappel qui doit prendre un nombre, et que nous voyons f(function(x) { /** ... */ });, alors x doit être un nombre").

Nom de l'opérateur Exemples de syntaxe Description
Nom du type {boolean}
{Window}
{goog.ui.Menu}
Spécifie le nom d'un type.
Type d'application {Array<string>}
 Tableau de chaînes.

{Object<string, number>}
Objet dans lequel les clés sont des chaînes et les valeurs sont des nombres.

Paramètre un type avec un ensemble d'arguments de type. Semblable aux génériques Java.
Union de types {(number|boolean)}
Nombre ou valeur booléenne.

Notez les parenthèses, qui sont obligatoires.
Indique qu'une valeur peut être de type A OU de type B.
Type d'enregistrement {{myNum: number, myObject}}
Type anonyme avec une propriété nommée myNum dont la valeur est de type number et une propriété nommée myObject dont la valeur est de n'importe quel type.

Indique que la valeur comporte les membres spécifiés avec des valeurs des types spécifiés.

Les accolades font partie de la syntaxe de type. Par exemple, pour désigner un Array d'objets ayant une propriété length, vous pouvez écrire :
Array<{length}>. Dans l'exemple de gauche, les accolades externes indiquent qu'il s'agit d'une expression de type, et les accolades internes indiquent qu'il s'agit d'un type d'enregistrement.

Type pouvant avoir une valeur nulle {?number}
Un nombre ou null.

Indique qu'une valeur est de type A ou null.

Tous les types d'objets sont annulables par défaut, qu'ils soient déclarés ou non avec l'opérateur Nullable. Un type d'objet est défini comme tout élément autre qu'une fonction, une chaîne, un nombre ou une valeur booléenne. Pour rendre un type d'objet non nullable, utilisez l'opérateur Non-nullable.

Type non nullable {!Object}
 : un objet, mais jamais la valeur null.

Indique qu'une valeur est de type A et n'est pas nulle.

Les fonctions et tous les types de valeurs (booléen, nombre et chaîne) sont non nullables par défaut, qu'ils soient déclarés ou non avec l'opérateur Non-nullable. Pour qu'un type de valeur ou de fonction puisse avoir une valeur nulle, utilisez l'opérateur Nullable.

Type de fonction {function(string, boolean)}
Fonction qui accepte deux paramètres (une chaîne et un booléen) et dont la valeur renvoyée est inconnue.
Spécifie une fonction et les types de paramètres de la fonction.
Type de retour de la fonction {function(): number}
Fonction qui ne prend aucun paramètre et renvoie un nombre.
Spécifie le type de valeur renvoyée d'une fonction.
Type de fonction this {function(this:goog.ui.Menu, string)}
Fonction qui accepte un paramètre (une chaîne) et s'exécute dans le contexte d'un goog.ui.Menu.
Spécifie le type de la valeur de this dans la fonction.
Type de fonction new {function(new:goog.ui.Menu, string)}
Fonction qui accepte un paramètre (une chaîne) et crée une instance de goog.ui.Menu lorsqu'elle est appelée avec le mot clé "new".
Spécifie le type construit d'un constructeur.
Paramètres variables {function(string, ...number): number}
Fonction qui accepte un paramètre (une chaîne), puis un nombre variable de paramètres qui doivent être des nombres.
Indique qu'un type de fonction accepte un nombre variable de paramètres et spécifie un type pour les paramètres variables.
Paramètres de variable (dans les annotations @param) @param {...number} var_args
Nombre variable de paramètres pour une fonction annotée.
Indique que la fonction annotée accepte un nombre variable de paramètres et spécifie un type pour les paramètres variables.
Paramètre facultatif dans une annotation @param @param {number=} opt_argument
Paramètre facultatif de type number.

Indique que l'argument décrit par une annotation @param est facultatif. Un appel de fonction peut omettre un argument facultatif. Un paramètre facultatif ne peut pas précéder un paramètre non facultatif dans la liste des paramètres.

Si un appel de méthode omet un paramètre facultatif, cet argument aura une valeur de undefined. Par conséquent, si la méthode stocke la valeur du paramètre dans une propriété de classe, la déclaration de type de cette propriété doit inclure une valeur possible de undefined, comme dans l'exemple suivant :

/**
 * 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;
}
Argument facultatif dans un type de fonction {function(?string=, number=)}
Fonction qui accepte une chaîne nullable facultative et un nombre facultatif comme arguments.
Indique qu'un argument dans un type de fonction est facultatif. Un argument facultatif peut être omis de l'appel de fonction. Dans la liste des arguments, un argument facultatif ne peut pas précéder un argument non facultatif.
Type ALL {*} Indique que la variable peut prendre n'importe quel type.
Type UNKNOWN {?} Indique que la variable peut prendre n'importe quel type et que le compilateur ne doit pas vérifier le type de ses utilisations.

Casting des types

Pour caster une valeur dans un type spécifique, utilisez la syntaxe suivante :

/** @type {!MyType} */ (valueExpression)
Les parenthèses autour de l'expression sont toujours obligatoires.

Types génériques

Tout comme Java, le compilateur Closure est compatible avec les types, fonctions et méthodes génériques. Les génériques fonctionnent sur des objets de différents types tout en préservant la sécurité des types au moment de la compilation.

Vous pouvez utiliser des génériques pour implémenter des collections généralisées qui contiennent des références à des objets d'un type particulier, ainsi que des algorithmes généralisés qui fonctionnent sur des objets d'un type particulier.

Déclarer un type générique

Un type peut être rendu générique en ajoutant une annotation @template au constructeur du type (pour les classes) ou à la déclaration d'interface (pour les interfaces). Exemple :

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

L'annotation @template T indique que Foo est un type générique avec un type de modèle, T. Le type de modèle T peut être utilisé comme type dans le champ d'application de la définition de Foo. Exemple :

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

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

La méthode get renvoie un objet de type T, et la méthode set n'accepte que les objets de type T.

Instancier un type générique

En réutilisant l'exemple ci-dessus, une instance basée sur un modèle Foo peut être créée de plusieurs manières :

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

Les deux instructions de constructeur ci-dessus créent une instance Foo dont le type de modèle T est string. Le compilateur s'assurera que les appels aux méthodes de foo et les accès aux propriétés de foo respectent le type de modèle. Exemple :

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

Les instances peuvent également être typées de manière implicite par leurs arguments de constructeur. Prenons un autre type générique, Bar :

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

Le type de l'argument du constructeur Bar est inféré comme string. Par conséquent, l'instance créée bar est inférée comme Bar<string>.

Plusieurs types de modèles

Un générique peut comporter un nombre illimité de types de modèles. La classe de carte suivante comporte deux types de modèles :

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

Tous les types de modèles pour un type générique doivent être spécifiés dans la même annotation @template, sous forme de liste séparée par des virgules. L'ordre des noms de types de modèles est important, car les annotations de type de modèle utiliseront l'ordre pour associer les types de modèles aux valeurs. Exemple :

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

Invariance des types génériques

Le compilateur Closure impose une saisie générique invariante. Cela signifie que si un contexte attend un type Foo<X>, vous ne pouvez pas transmettre un type Foo<Y> lorsque X et Y sont des types différents, même si l'un est un sous-type de l'autre. Exemple :

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

Héritage des types génériques

Les types génériques peuvent être hérités, et leurs types de modèles peuvent être fixes ou propagés au type hérité. Voici un exemple de type hérité corrigeant le type de modèle de son supertype :

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

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

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

En étendant A<string>, B aura une méthode method qui prend un paramètre de type string.

Voici un exemple de type hérité propageant le type de modèle de son supertype :

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

En étendant A<U>, les instances de C basées sur un modèle auront une méthode method qui accepte un paramètre de type de modèle U.

Les interfaces peuvent être implémentées et étendues de manière similaire, mais un seul type ne peut pas implémenter la même interface plusieurs fois avec différents types de modèles. Exemple :

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

Fonctions et méthodes génériques

Comme les types génériques, les fonctions et les méthodes peuvent être rendues génériques en ajoutant une annotation @template à leur définition. Exemple :

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