Annotare JavaScript per Closure Compiler

Nota: questa pagina è obsoleta. L'elenco completo è disponibile all'indirizzo https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler

Panoramica

Closure Compiler può utilizzare le informazioni sul tipo di dati delle variabili JavaScript per fornire avvisi e ottimizzazioni avanzate. JavaScript, tuttavia, non ha modo di dichiarare i tipi.

Poiché JavaScript non ha una sintassi per dichiarare il tipo di una variabile, devi utilizzare i commenti nel codice per specificare il tipo di dati.

Il linguaggio dei tipi di Closure Compiler deriva dalle annotazioni utilizzate dallo strumento di generazione della documentazione JSDoc, anche se da allora è cambiato. Ora include diverse annotazioni che JSDoc non supporta e viceversa. Questo documento descrive l'insieme di annotazioni ed espressioni di tipo che Closure Compiler comprende.

  1. Tag JSDoc
  2. Type Expressions
  3. Tipi generici

Tag JSDoc

Closure Compiler cerca le informazioni sui tipi nei tag JSDoc. Utilizza i tag JSDoc descritti nella tabella di riferimento riportata di seguito per aiutare il compilatore a ottimizzare il codice e a verificare la presenza di possibili errori di tipo e altri errori.

Questa tabella include solo i tag che influiscono sul comportamento del compilatore Closure. Per informazioni su altri tag JSDoc, consulta la documentazione di JSDoc Toolkit.

Tag Descrizione
@abstract

Contrassegna un metodo come astratto. Analogamente all'impostazione di un metodo su goog.abstractMethod, il compilatore può eliminare i metodi annotati con @abstract per ridurre le dimensioni del codice.

Il compilatore genera un avviso se un metodo contrassegnato con @abstract ha un'implementazione non vuota.

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

Contrassegna una variabile come di sola lettura. Il compilatore può incorporare le variabili @const, il che ottimizza il codice JavaScript.

La dichiarazione del tipo è facoltativa.

Il compilatore genera un avviso se a una variabile contrassegnata con @const viene assegnato un valore più di una volta. Se la variabile è un oggetto, tieni presente che il compilatore non vieta le modifiche alle proprietà dell'oggetto.

Ad esempio:
/** @const */ var MY_BEER = 'stout';

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

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

Contrassegna una funzione come costruttore. Il compilatore richiede un'annotazione @constructor per qualsiasi funzione utilizzata con la parola chiave new

Ad esempio:

/**
 * A rectangle.
 * @constructor
 */
function GM_Rect() {
  ...
}
@define Indica una costante che può essere sostituita dal compilatore in fase di compilazione. Con l'esempio a sinistra, puoi passare il flag --define='ENABLE_DEBUG=false' al compilatore per modificare il valore di ENABLE_DEBUG in false. Il tipo di una costante definita può essere numerico, stringa o booleano. Le definizioni sono consentite solo nell'ambito globale.

Ad esempio:

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

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

Contrassegna una funzione, un metodo o una proprietà in modo che il relativo utilizzo generi un avviso del compilatore che indica che non deve più essere utilizzata.

Ad esempio:

/**
 * 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 viene utilizzato per creare oggetti con un numero variabile di proprietà. Quando un costruttore (Foo nell'esempio) è annotato con @dict, puoi utilizzare solo la notazione tra parentesi per accedere alle proprietà degli oggetti Foo. L'annotazione può essere utilizzata anche direttamente sui valori letterali degli oggetti.

Ad esempio:

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

Specifica il tipo di un'enumerazione. Un'enumerazione è un oggetto le cui proprietà costituiscono un insieme di costanti correlate. Il tag @enum deve essere seguito da un'espressione di tipo.

L'etichetta del tipo di un'enumerazione si applica a ogni proprietà dell'enumerazione. Ad esempio, se un'enumerazione ha il tipo number, ogni proprietà enumerata deve essere un numero. Se il tipo di un'enumerazione viene omesso, viene utilizzato il valore number.

Ad esempio:

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

Dato questo codice

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

quando il compilatore viene eseguito con il flag --generate_exports, genererà il codice:

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

che esporterà i simboli nel codice non compilato. Puoi scrivere /** @export {SomeType} */ come abbreviazione di

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

Il codice che utilizza l'annotazione @export deve

  1. includi closure/base.js o
  2. definisci sia goog.exportSymbol che goog.exportProperty con la stessa firma del metodo nel proprio codebase.
@extends

Contrassegna una classe o un'interfaccia come ereditata da un'altra classe. Una classe contrassegnata con @extends deve essere contrassegnata anche con @constructor o @interface.

Nota: @extends non fa sì che un corso venga ereditato da un altro corso. L'annotazione indica semplicemente al compilatore che può trattare una classe come sottoclasse di un'altra durante il controllo dei tipi.

Per un esempio di implementazione dell'ereditarietà, consulta la funzione goog.inherits() della libreria Closure.

Ad esempio:

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

Indica che questo corso non può essere esteso. Per i metodi, indica che nessuna sottoclasse può eseguire l'override di quel metodo.

Ad esempio:

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

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

Utilizzato con @constructor per indicare che una classe implementa un'interfaccia.

Il compilatore genera un avviso se contrassegni un costruttore con @implements e poi non implementi tutti i metodi e le proprietà definiti dall'interfaccia.

Ad esempio:

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

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

Questa annotazione può essere visualizzata solo nelle dichiarazioni di proprietà esterne. La proprietà ha un tipo dichiarato, ma puoi assegnarle qualsiasi tipo senza ricevere un avviso. Quando accedi alla proprietà, ricevi un valore del tipo dichiarato. Ad esempio, element.innerHTML può essere assegnato a qualsiasi tipo, ma restituirà sempre una stringa.

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

Indica che un metodo o una proprietà di una sottoclasse nasconde intenzionalmente un metodo o una proprietà della superclasse e ha esattamente la stessa documentazione. Tieni presente che il tag @inheritDoc implica il tag @override.

Ad esempio:

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

Contrassegna una funzione come interfaccia. Un'interfaccia specifica i membri obbligatori di un tipo. Qualsiasi classe che implementa un'interfaccia deve implementare tutti i metodi e le proprietà definiti nel prototipo dell'interfaccia. Vedi @implements.

Il compilatore verifica che le interfacce non vengano istanziate. Se la parola chiave new viene utilizzata con una funzione di interfaccia, il compilatore genera un avviso.

Ad esempio:

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

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

Indica che le chiavi di un oggetto letterale devono essere trattate come proprietà di un altro oggetto. Questa annotazione deve essere visualizzata solo nei valori letterali oggetto.

Tieni presente che il nome tra parentesi graffe non è un nome di tipo come in altre annotazioni. È il nome di un oggetto. Indica l'oggetto a cui vengono prestate le proprietà. Ad esempio, @type {Foo} significa "un'istanza di Foo", mentre @lends {Foo} significa "il costruttore Foo".

La documentazione del toolkit JSDoc contiene ulteriori informazioni su questa annotazione.

Ad esempio:

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

Indica al compilatore di inserire il commento associato prima del codice compilato per il file contrassegnato. Questa annotazione consente di mantenere invariate le note importanti (come licenze legali o testo del copyright) durante la compilazione. Le interruzioni di riga vengono mantenute.

Ad esempio:

/**
 * @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 una proprietà che non deve essere compressa dal compilatore in una variabile. L'utilizzo principale di @nocollapse è consentire l'esportazione di proprietà modificabili. Tieni presente che le proprietà non compresse possono comunque essere rinominate dal compilatore. Se annoti una proprietà che è un oggetto con @nocollapse, tutte le relative proprietà rimarranno espanse.

Ad esempio:

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

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

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

Indica che una chiamata alla funzione esterna dichiarata non ha effetti collaterali. Questa annotazione consente al compilatore di rimuovere le chiamate alla funzione se il valore restituito non viene utilizzato. L'annotazione è consentita solo in extern files.

Ad esempio:

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

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

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

Indica che un metodo o una proprietà di una sottoclasse nasconde intenzionalmente un metodo o una proprietà della superclasse. Se non sono incluse altre annotazioni, il metodo o la proprietà eredita automaticamente le annotazioni dalla superclasse.

Ad esempio:

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

Contrassegna un membro o una proprietà come privato del pacchetto. Solo il codice nella stessa directory può accedere ai nomi contrassegnati con @package. In particolare, il codice nelle directory principali e secondarie non può accedere ai nomi contrassegnati con @package.

I costruttori pubblici possono avere @package proprietà per limitare i metodi che i chiamanti esterni alla directory possono utilizzare. D'altra parte, i costruttori @package possono avere proprietà pubbliche per impedire ai chiamanti esterni alla directory di creare direttamente un'istanza di un tipo.

Ad esempio:

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

Utilizzato con definizioni di metodi, funzioni e costruttori per specificare i tipi di argomenti della funzione. I tag @param devono essere nello stesso ordine dei parametri nella definizione della funzione.

Il tag @param deve essere seguito da un'espressione di tipo.

In alternativa, puoi annotare i tipi di parametri in linea (vedi la funzione foo nell'esempio).

Ad esempio:

/**
 * 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;
}
Per i parametri che sono un pattern di destrutturazione, puoi utilizzare qualsiasi nome che sia un identificatore JS valido, dopo l'annotazione del tipo.
/**
 * @param {{name: string, age: number}} person
 */
function logPerson({name, age}) {
  console.log(`${name} is ${age} years old`);
}
@private

Contrassegna un membro come privato. Solo il codice nello stesso file può accedere a variabili e funzioni globali contrassegnate con @private. I costruttori contrassegnati con @private possono essere istanziati solo dal codice nello stesso file e dai relativi membri statici e di istanza.

È possibile accedere ovunque anche alle proprietà statiche pubbliche dei costruttori contrassegnati con @private e l'operatore instanceof può sempre accedere ai membri @private.

Ad esempio:

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

Indica che un membro o una proprietà è protetto.

Una proprietà contrassegnata con @protected è accessibile a:

  • tutto il codice nello stesso file
  • metodi statici e metodi di istanza di qualsiasi sottoclasse della classe su cui è definita la proprietà.

Ad esempio:

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

Contrassegna una funzione come interfaccia strutturale. Un'interfaccia strutturale è simile a un tipo nominale @interface, ma consente implementazioni implicite. Ciò significa che qualsiasi classe che include i metodi e le proprietà definiti nel prototipo dell'interfaccia strutturale implementa l'interfaccia strutturale, indipendentemente dal fatto che utilizzi il tag @implements. Anche i tipi di record e i valori letterali degli oggetti implementano implicitamente un'interfaccia strutturale se contengono le proprietà richieste.

Ad esempio:

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

Specifica i tipi restituiti delle definizioni di metodi e funzioni. Il tag @return deve essere seguito da un'espressione di tipo.

In alternativa, puoi annotare il tipo restituito inline (vedi la funzione foo nell'esempio).

Se una funzione che non è in externs non ha un valore restituito, puoi omettere il tag @return e il compilatore presupporrà che la funzione restituisca undefined.

Ad esempio:

/**
 * 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 viene utilizzato per creare oggetti con un numero fisso di proprietà. Quando un costruttore (Foo nell'esempio) è annotato con @struct, puoi utilizzare solo la notazione con il punto per accedere alle proprietà degli oggetti Foo, non la notazione con le parentesi. Inoltre, non puoi aggiungere una proprietà a un'istanza Foo dopo la sua creazione. L'annotazione può essere utilizzata anche direttamente sui valori letterali degli oggetti.

Ad esempio:

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

Vedi Tipi generici.

Ad esempio:

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

Specifica il tipo di oggetto a cui fa riferimento la parola chiave this all'interno di una funzione. Il tag @this deve essere seguito da un'espressione di tipo.

Per evitare avvisi del compilatore, devi utilizzare un'annotazione @this ogni volta che this viene visualizzato in una funzione che non è un metodo prototipo né una funzione contrassegnata come @constructor.

Ad esempio:

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

Utilizzato per documentare le eccezioni generate da una funzione. Il controllo dei tipi non utilizza attualmente queste informazioni. Viene utilizzato solo per capire se una funzione dichiarata in un file externs ha effetti collaterali.

Ad esempio:

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

Identifica il tipo di variabile, proprietà o espressione. Il tag @type deve essere seguito da un'espressione di tipo.

Quando dichiari una variabile o un parametro di funzione, puoi scrivere l'annotazione di tipo in linea omettendo {} e @type, come nel secondo esempio. Questa scorciatoia può essere utilizzata solo nei punti in cui viene dichiarata una variabile o un parametro di funzione. Se vuoi modificare il tipo in un secondo momento, avrai bisogno di un cast di tipo.

Ad esempio:

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

Dichiara un alias per un tipo più complesso. Al momento, i typedef possono essere definiti solo a livello principale, non all'interno delle funzioni. Abbiamo corretto questa limitazione nella nuova inferenza dei tipi.

Ad esempio:

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

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

Indica che un corso non è di tipo @struct né di tipo @dict. Questa è l'impostazione predefinita, quindi in genere non è necessario scriverla esplicitamente, a meno che non utilizzi la parola chiave class, che produce classi che sono @struct per impostazione predefinita.

Ad esempio:

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

Espressioni di tipo

Puoi specificare il tipo di dati di qualsiasi variabile, proprietà, espressione o parametro di funzione con un'espressione di tipo. Un'espressione di tipo è costituita da parentesi graffe ("{ }") contenenti una combinazione degli operatori di tipo descritti di seguito.

Utilizza un'espressione di tipo con il tag @param per dichiarare il tipo di un parametro di funzione. Utilizza un'espressione di tipo con il tag @type per dichiarare il tipo di una variabile, una proprietà o un'espressione.

Più tipi specifichi nel codice, più ottimizzazioni può apportare il compilatore e più errori può rilevare.

Il compilatore utilizza queste annotazioni per controllare il tipo del programma. Tieni presente che Closure Compiler non garantisce di poter determinare il tipo di ogni espressione nel tuo programma. Fa del suo meglio esaminando il modo in cui vengono utilizzate le variabili e le annotazioni di tipo associate alle relative dichiarazioni. Poi, utilizza una serie di algoritmi di inferenza dei tipi per determinare il tipo di quante più espressioni possibili. Alcuni di questi algoritmi sono semplici ("se x è un numero e vediamo y = x;, allora y è un numero"). Alcuni sono più indiretti ("se il primo parametro di f's è documentato come callback che deve accettare un numero e vediamo f(function(x) { /** ... */ });, allora x deve essere un numero").

Nome operatore Esempi di sintassi Descrizione
Digita nome {boolean}
{Window}
{goog.ui.Menu}
Specifica il nome di un tipo.
Type Application {Array<string>}
Un array di stringhe.

{Object<string, number>}
Un oggetto in cui le chiavi sono stringhe e i valori sono numeri.

Parametrizza un tipo con un insieme di argomenti di tipo. Simile ai generici Java.
Type Union {(number|boolean)}
Un numero o un valore booleano.

Nota le parentesi, che sono obbligatorie.
Indica che un valore potrebbe essere di tipo A O di tipo B.
Tipo di record {{myNum: number, myObject}}
Un tipo anonimo con una proprietà denominata myNum con un valore di tipo number e una proprietà denominata myObject con un valore di qualsiasi tipo.

Indica che il valore ha i membri specificati con valori dei tipi specificati.

Le parentesi graffe fanno parte della sintassi del tipo. Ad esempio, per indicare un Array di oggetti che hanno una proprietà length, potresti scrivere:
Array<{length}>. Nell'esempio a sinistra, le parentesi graffe esterne indicano che si tratta di un'espressione di tipo e quelle interne indicano che si tratta di un tipo di record.

Tipo nullable {?number}
Un numero o null.

Indica che un valore è di tipo A o null.

Per impostazione predefinita, tutti i tipi di oggetti sono annullabili, indipendentemente dal fatto che siano dichiarati con l'operatore Nullable. Un tipo di oggetto è definito come qualsiasi cosa tranne una funzione, una stringa, un numero o un valore booleano. Per rendere un tipo di oggetto non annullabile, utilizza l'operatore Non annullabile.

Tipo non annullabile {!Object}
Un oggetto, ma mai il valore null.

Indica che un valore è di tipo A e non nullo.

Per impostazione predefinita, le funzioni e tutti i tipi di valori (booleano, numerico e stringa) non sono annullabili, indipendentemente dal fatto che siano dichiarati o meno con l'operatore Non-nullable. Per rendere annullabile un tipo di valore o funzione, utilizza l'operatore Nullable.

Tipo di funzione {function(string, boolean)}
Una funzione che accetta due parametri (una stringa e un valore booleano) e ha un valore restituito sconosciuto.
Specifica una funzione e i tipi di parametri della funzione.
Tipo restituito della funzione {function(): number}
Una funzione che non accetta parametri e restituisce un numero.
Specifica il tipo di valore restituito di una funzione.
Tipo di funzione this {function(this:goog.ui.Menu, string)}
Una funzione che accetta un parametro (una stringa) ed esegue nel contesto di goog.ui.Menu.
Specifica il tipo di valore di this all'interno della funzione.
Tipo di funzione new {function(new:goog.ui.Menu, string)}
Una funzione che accetta un parametro (una stringa) e crea una nuova istanza di goog.ui.Menu quando viene chiamata con la parola chiave "new".
Specifica il tipo costruito di un costruttore.
Parametri variabili {function(string, ...number): number}
Una funzione che accetta un parametro (una stringa) e poi un numero variabile di parametri che devono essere numeri.
Indica che un tipo di funzione accetta un numero variabile di parametri e specifica un tipo per i parametri variabili.
Parametri variabili (nelle annotazioni @param) @param {...number} var_args
Un numero variabile di parametri per una funzione annotata.
Indica che la funzione annotata accetta un numero variabile di parametri e specifica un tipo per i parametri variabili.
Parametro facoltativo in un'annotazione @param @param {number=} opt_argument
Un parametro facoltativo di tipo number.

Indica che l'argomento descritto da un'annotazione @param è facoltativo. Una chiamata di funzione può omettere un argomento facoltativo. Un parametro facoltativo non può precedere un parametro non facoltativo nell'elenco dei parametri.

Se una chiamata al metodo omette un parametro facoltativo, l'argomento avrà un valore di undefined. Pertanto, se il metodo memorizza il valore del parametro in una proprietà della classe, la dichiarazione di tipo di questa proprietà deve includere un valore possibile di undefined, come nel seguente esempio:

/**
 * 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;
}
Argomento facoltativo in un tipo di funzione {function(?string=, number=)}
Una funzione che accetta una stringa facoltativa e nullabile e un numero facoltativo come argomenti.
Indica che un argomento in un tipo di funzione è facoltativo. Un argomento facoltativo può essere omesso dalla chiamata alla funzione. Un argomento facoltativo non può precedere un argomento non facoltativo nell'elenco degli argomenti.
Il tipo ALL {*} Indica che la variabile può assumere qualsiasi tipo.
Tipo UNKNOWN {?} Indica che la variabile può assumere qualsiasi tipo e che il compilatore non deve eseguire il controllo del tipo per nessuno dei suoi utilizzi.

Type Casting

Per eseguire il cast di un valore in un tipo specifico, utilizza questa sintassi

/** @type {!MyType} */ (valueExpression)
Le parentesi intorno all'espressione sono sempre obbligatorie.

Tipi generici

Proprio come Java, Closure Compiler supporta tipi, funzioni e metodi generici. I generici operano su oggetti di vari tipi preservando la sicurezza dei tipi in fase di compilazione.

Puoi utilizzare i generici per implementare raccolte generalizzate che contengono riferimenti a oggetti di un tipo specifico e algoritmi generalizzati che operano su oggetti di un tipo specifico.

Dichiarazione di un tipo generico

Un tipo può essere reso generico aggiungendo un'annotazione @template al costruttore del tipo (per le classi) o alla dichiarazione dell'interfaccia (per le interfacce). Ad esempio:

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

L'annotazione @template T indica che Foo è un tipo generico con un tipo di modello, T. Il tipo di modello T può essere utilizzato come tipo nell'ambito della definizione di Foo. Ad esempio:

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

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

Il metodo get restituirà un oggetto di tipo T, mentre il metodo set accetterà solo oggetti di tipo T.

Creazione di un'istanza di un tipo generico

Se riutilizziamo l'esempio precedente, è possibile creare un'istanza basata su modello di Foo in diversi modi:

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

Entrambe le istruzioni del costruttore precedenti creano un'istanza Foo il cui tipo di modello T è string. Il compilatore impone che le chiamate ai metodi di foo e gli accessi alle proprietà di foo rispettino il tipo basato su modello. Ad esempio:

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

Le istanze possono anche essere digitate implicitamente dagli argomenti del costruttore. Prendi in considerazione un altro tipo generico, Bar:

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

Il tipo dell'argomento del costruttore Bar viene dedotto come string e, di conseguenza, l'istanza creata bar viene dedotta come Bar<string>.

Più tipi di modello

Un generico può avere un numero qualsiasi di tipi di modelli. La seguente classe di mappe ha due tipi di modello:

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

Tutti i tipi di modelli per un tipo generico devono essere specificati nella stessa annotazione @template, come elenco separato da virgole. L'ordine dei nomi dei tipi di modelli è importante, poiché le annotazioni dei tipi di modelli utilizzeranno l'ordine per accoppiare i tipi di modelli con i valori. Ad esempio:

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

Invarianza dei tipi generici

Closure Compiler applica la digitazione generica invariante. Ciò significa che se un contesto prevede un tipo Foo<X>, non puoi passare un tipo Foo<Y> quando X e Y sono tipi diversi, anche se uno è un sottotipo dell'altro. Ad esempio:

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

Eredità dei tipi generici

I tipi generici possono essere ereditati e i relativi tipi di modelli possono essere fissi o propagati al tipo ereditato. Ecco un esempio di un tipo ereditato che corregge il tipo di modello del relativo supertipo:

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

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

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

Estendendo A<string>, B avrà un metodo method che accetta un parametro di tipo string.

Ecco un esempio di un tipo ereditato che propaga il tipo di modello del suo supertipo:

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

Estendendo A<U>, le istanze basate su modelli di C avranno un metodo method che accetta un parametro del tipo di modello U.

Le interfacce possono essere implementate ed estese in modo simile, ma un singolo tipo non può implementare la stessa interfaccia più volte con tipi di modelli diversi. Ad esempio:

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

Funzioni e metodi generici

Analogamente ai tipi generici, le funzioni e i metodi possono essere resi generici aggiungendo un'annotazione @template alla loro definizione. Ad esempio:

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