Nota: questa pagina non è aggiornata. L'elenco completo è disponibile all'indirizzo https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler
Panoramica
Il compilatore può utilizzare le informazioni sul tipo di dati relative alle variabili JavaScript per fornire un sistema di ottimizzazione e avvisi avanzati. JavaScript, tuttavia, non ha alcun modo per dichiarare i tipi.
Poiché JavaScript non ha sintassi per dichiarare il tipo di una variabile, devi utilizzare i commenti nel codice per specificare il tipo di dati.
Il linguaggio del tipo di Closure Compiler deriva dalle annotazioni utilizzate dallo strumento per la generazione di documenti JSDoc, anche se da allora si è discostato. Ora include diverse annotazioni non supportate da JSDoc e viceversa. Questo documento descrive l'insieme di annotazioni e di espressioni di tipo comprese dal compilatore.
Tag JSDoc
Il compilatore cerca informazioni sui tipi di tag JSDoc. Utilizza i tag JSDoc descritti nella tabella di riferimento in basso per aiutare il compilatore a ottimizzare il codice e a verificare la presenza di eventuali errori di tipo e altri errori.
Questa tabella include solo i tag che influiscono sul comportamento del compilatore di chiusura. Per informazioni sugli altri tag JSDoc, consulta la documentazione di JSDoc Toolkit.
Tag | Descrizione |
---|---|
@abstract
|
Contrassegna un metodo come astratto. Analogamente all'impostazione di un metodo su
Il compilatore genera un avviso se un metodo contrassegnato con /** @abstract */ foo.MyClass.prototype.abstractMethod = function() {}; |
@const
|
Contrassegna una variabile come di sola lettura. Il compilatore può incorporare variabili La dichiarazione del tipo è facoltativa.
Il compilatore genera un avviso se a una variabile contrassegnata con /** @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 Ad esempio: /** * A rectangle. * @constructor */ function GM_Rect() { ... } |
@define
|
Indica una costante che può essere sostituita dal compilatore al momento della compilazione.
Con l'esempio a sinistra, puoi passare il flag --define='ENABLE_DEBUG=false' al compilatore per cambiare il valore di ENABLE_DEBUG in false.
Il tipo di una costante definita può essere un numero, una stringa o un valore 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à per fare in modo che, utilizzandola, venga generato un avviso del compilatore che indica che non deve più essere utilizzato. 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
|
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 L'etichetta del tipo di un'enumerazione si applica a ogni proprietà dell'enumerazione. Ad esempio, se un'enumerazione ha il tipo 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 goog.exportProperty(foo.MyPublicClass.prototype, 'myPublicMethod', foo.MyPublicClass.prototype.myPublicMethod); che esporteranno i simboli nel codice non compilato. Puoi scrivere
/** * @export * @type {SomeType} */ Il codice che utilizza l'annotazione
|
@extends
|
Contrassegna una classe o un'interfaccia come ereditata da un'altra classe. Una classe contrassegnata con
Nota:
Per un esempio di implementazione dell'ereditarietà, consulta la funzione Chiusura della libreria Ad esempio: /** * Immutable empty node list. * @constructor * @extends {goog.ds.BasicNodeList} */ goog.ds.EmptyNodeList = function() { ... }; |
@final
|
Indica che non è consentito estendere il corso. Per i metodi, indica che a nessuna sottoclasse è consentito sostituire 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
Il compilatore genera un avviso se tagghi un costruttore con Ad esempio: /** * A shape. * @interface */ function Shape() {}; Shape.prototype.draw = function() {}; /** * @constructor * @implements {Shape} */ function Square() {}; Square.prototype.draw = function() { ... }; |
@implicitCast
|
Questa annotazione può apparire solo nelle dichiarazioni delle proprietà esterne.
La proprietà ha un tipo dichiarato, ma puoi assegnarle qualsiasi tipo senza avviso. Quando accedi alla proprietà, ti viene restituito
un valore del tipo dichiarato. Ad esempio, puoi assegnare /** * @type {string} * @implicitCast */ Element.prototype.innerHTML; |
@inheritDoc
|
Indica che un metodo o una proprietà di una sottoclasse nasconde di proposito un metodo o una proprietà di tale classe e ha esattamente la stessa documentazione. Tieni presente che il tag 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. Consulta
Il compilatore verifica che non sia stata creata un'istanza delle interfacce. Se la parola chiave 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 del valore letterale di un oggetto devono essere considerate come proprietà di un altro oggetto. Questa annotazione deve essere visualizzata solo nei valori letterali degli oggetti.
Tieni presente che il nome tra parentesi graffe non è un nome tipo come
nelle altre annotazioni. È il nome di un oggetto. Denomina l'oggetto a cui vengono prestate le proprietà.
Ad esempio, Per ulteriori informazioni su questa annotazione, consulta la documentazione relativa a JSDoc. 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 compilazioni di notifiche legali (come licenze legali o testo del copyright). Le interruzioni di riga vengono conservate. 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 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
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à ereditano automaticamente le annotazioni dalla relativa 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 pacchetto privato. Solo il codice nella stessa directory può accedere ai nomi contrassegnati con
I costruttori pubblici possono avere proprietà 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 le definizioni di metodo, funzione e costruttore per specificare i tipi di argomenti della funzione. I tag
Il tag
In alternativa, puoi annotare i tipi di parametri in linea
(vedi la funzione 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 rappresentano un pattern distruttivo, 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 alle variabili e alle funzioni globali contrassegnate con
Puoi accedere ovunque alle proprietà statiche pubbliche dei costruttori contrassegnate con Ad esempio: /** * Handlers that are listening to this logger. * @private {Array<Function>} */ this.handlers_ = []; |
@protected
|
Indica che un membro o una proprietà sono protetti.
Una proprietà contrassegnata come
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 una 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 di ritorno delle definizioni di metodo e funzione.
Il tag
In alternativa, puoi annotare il tipo di ritorno incorporato (vedi la funzione
Se una funzione che non è in uso non ha un valore restituito, puoi omettere il tag 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
|
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
Per evitare avvisi del compilatore, devi utilizzare un'annotazione 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. Al momento il controllore del tipo non utilizza queste informazioni. Viene utilizzato solo per capire se una funzione dichiarata in un file di tipo esterno ha effetti collaterali. Ad esempio: /** * @throws {DOMException} */ DOMApplicationCache.prototype.swapCache = function() { ... }; |
@type
|
Identifica il tipo di variabile, proprietà o espressione. Il tag Quando dichiari una variabile o un parametro di funzione, puoi scrivere l'annotazione del tipo in linea omettendo 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, typedefs può essere definito solo al livello superiore, non all'interno delle funzioni. Abbiamo corretto questo limite nell'inferenza del nuovo tipo. Ad esempio: /** @typedef {(string|number)} */ goog.NumberLike; /** @param {goog.NumberLike} x A number or a string. */ goog.readNumber = function(x) { ... } |
@unrestricted
|
Indica che una classe non è di tipo 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 |
Tipo di espressioni
Puoi specificare il tipo di dati di qualsiasi variabile, proprietà, espressione o parametro della funzione con un'espressione di tipo. Un'espressione di tipo è composta da parentesi graffe ("{ }") contenenti una combinazione di operatori di tipo descritti di seguito.
Utilizza un'espressione di tipo con il tag @param
per dichiarare il tipo di un parametro della funzione. Utilizza un'espressione di tipo con il tag @type
per dichiarare il tipo di una variabile, una proprietà o un'espressione.
Maggiore è il numero di tipi specificati nel codice, maggiori sono le ottimizzazioni che il compilatore può eseguire e il numero di errori che può riscontrare.
Il compilatore utilizza queste annotazioni per eseguire il controllo tipo del programma.
Tieni presente che il componente Closure Compiler non fa alcuna promessa che possa stabilire il tipo di ogni espressione nel programma. Per ottenere i migliori risultati, è possibile controllare come vengono utilizzate le variabili e le annotazioni dei tipi allegate alle relative dichiarazioni. Quindi, utilizza una serie di algoritmi di deduzione di tipo per
determinare il tipo di quante possibili espressioni. 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 è documentato come un callback che deve contenere un numero, e vediamo f(function(x) { /** ... */ });
, allora x deve essere un numero").
Nome operatore | Esempi di sintassi | Descrizione |
---|---|---|
Nome del tipo |
{boolean} {Window} {goog.ui.Menu}
|
Specifica il nome di un tipo. |
Tipo di applicazione |
{Array<string>} Una matrice di stringhe.
|
Parametrizza un tipo con un insieme di argomenti type. Simile ai generici Java. |
Tipo Union |
{(number|boolean)} Un numero o un valore booleano. Prendi nota delle parentesi necessarie. |
Indica che un valore potrebbe essere di tipo A o tipo B. |
Tipo di record |
{{myNum: number, myObject}}
Un tipo anonimo con una proprietà denominata myNum
che ha un valore di tipo number e una proprietà denominata
myObject che ha un valore di qualsiasi tipo.
|
Indica che il valore contiene i membri specificati con valori dei tipi specificati. Le parentesi graffe fanno parte della sintassi dei tipi. Ad esempio, per indicare un |
Tipo null |
{?number} Un numero o null .
|
Indica che un valore è di tipo A o Tutti i tipi di oggetti sono nulli per impostazione predefinita, sia che siano dichiarati o meno con l'operatore Nullable. Per tipo di oggetto si intende qualsiasi elemento tranne una funzione, una stringa, un numero o un valore booleano. Per rendere non valido un tipo di oggetto, utilizza l'operatore Non-nullable. |
Tipo non null |
{!Object} Un oggetto, ma mai il valore null .
|
Indica che il valore A è di tipo A e non null. Le funzioni e tutti i tipi di valori (booleani, numeri e stringhe) non sono null per impostazione predefinita, indipendentemente dal fatto che siano dichiarati o meno con l'operatore Non nullable. Per rendere nullo un valore o un tipo di 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 dei parametri della funzione. |
Tipo restituito funzione |
{function(): number} Una funzione che non accetta parametri e restituisce un numero. |
Specifica il tipo di valore di ritorno di una funzione. |
Tipo di funzione this |
{function(this:goog.ui.Menu, string)} Una funzione che accetta un solo parametro (una stringa) ed esegue nel contesto di goog.ui.Menu. |
Specifica il tipo del 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 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 della variabile. |
Parametri variabili (in @param annotazioni)
|
@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 della variabile. |
Parametro facoltativo in un'annotazione @param
|
@param {number=} opt_argument Un parametro facoltativo di tipo number .
|
Indica che l'argomento descritto da
un'annotazione
Se in una chiamata di metodo viene omesso un parametro facoltativo, l'argomento avrà un valore /** * 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 come stringa facoltativa e un numero facoltativo come argomenti. |
Indica che un argomento in un tipo di funzione è facoltativo. Un argomento facoltativo può essere omesso dalla chiamata della funzione. Un argomento facoltativo non può precedere un argomento non facoltativo nell'elenco di argomenti. |
Il tipo ALL | {*} |
Indica che la variabile può assumere qualsiasi tipo. |
Il tipo UNKNOWN | {?} |
Indica che la variabile può assumere qualsiasi tipo e che il compilatore non deve eseguire controlli di digitazione per qualsiasi utilizzo. |
Tipo di trasmissione
Per trasmettere un valore a un tipo specifico, utilizza questa sintassi
/** @type {!MyType} */ (valueExpression)Le parentesi attorno all'espressione sono sempre obbligatorie.
Tipi generici
Proprio come Java, il componente Closure Compiler supporta tipi, funzioni e metodi generici. I generici utilizzano oggetti di diversi tipi preservando la sicurezza dei tipi di tempo.
Puoi utilizzare i generici per implementare raccolte generalizzate che contengono riferimenti a oggetti di un certo tipo e algoritmi generalizzati che operano su oggetti di un certo tipo.
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
.
È possibile utilizzare il tipo di modello T
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
e il metodo set
accetterà solo oggetti di tipo T
.
Creazione dell'istanza di un tipo generico
Riutilizzando l'esempio precedente, puoi creare un'istanza 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 modello T
è string
. Il compilatore impone che le chiamate ai metodi di foo
e l'accesso alle proprietà di foo
rispettano 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 implicite digitate dai rispettivi argomenti costruttori.
Considera un tipo generico diverso, Bar
:
/** * @param {T} t * @constructor * @template T */ Bar = function(t) { ... }; var bar = new Bar("hello"); // bar is a Bar<string>
Il tipo di argomento del costruttore Bar
viene dedotto
come string
, pertanto l'istanza creata
bar
viene dedotta come Bar<string>
.
Più tipi di modello
Un generico può avere un numero qualsiasi di tipi di modello. La seguente classe di mappa ha due tipi di modello:
/** * @constructor * @template Key, Val */ MyMap = function() { ... };
Tutti i tipi di modello per un tipo generico devono essere specificati nella stessa annotazione @template
di un elenco separato da virgole. L'ordine dei nomi dei tipi di modello è importante, poiché le annotazioni dei tipi basati su modelli utilizzeranno l'ordine per abbinare i tipi di modello ai valori. Ad esempio:
/** @type {MyMap<string, number>} */ var map; // Key = string, Val = number.
Invarianza di tipi generici
Il compilatore Closure 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
Ereditarietà dei tipi generici
I tipi generici possono essere ereditati e i loro tipi di modello possono essere fissi o propagati al tipo ereditato. Di seguito è riportato 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() { ... };
Se estendi 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() { ... };
L'estensione di A<U>
alle istanze modello di C
avrà un metodo method
che accetta un parametro del tipo 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
In modo simile a quelle generiche, le funzioni e i metodi possono essere resi generici aggiungendo
un'annotazione @template
alla relativa 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