Menu

instanceof in TypeScript: restringere classi ed errori

instanceof controlla a runtime se un oggetto è stato creato da una classe, e TypeScript restringe la variabile a quella classe. Come funziona con le tue classi e con le sottoclassi di Error, perché non può controllare interfacce o alias di tipo, e dove dà risposte sorprendenti.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

value instanceof SomeClass è un controllo JavaScript eseguito a runtime: è true quando SomeClass.prototype si trova nella catena dei prototipi dell'oggetto, cioè quando l'oggetto è stato creato con new SomeClass (o con una sottoclasse). TypeScript restringe value a SomeClass dentro il controllo.

Usa instanceof per gli oggetti creati da classi, e typeof per i primitivi. Qui servono entrambi: instanceof separa il Date, poi typeof divide ciò che resta.

Restringere le tue classi

instanceof restringe alla classe nel ramo true e la toglie nel ramo false, quindi un'unione di classi si può gestire un membro alla volta.

Anche un'istanza di una sottoclasse supera il controllo per il genitore: se Square extends Rect, allora new Square(2) instanceof Rect è true. Quando i rami sono diversi, controlla prima la classe più specifica.

Sottoclassi di Error nel catch

L'uso più comune di instanceof è nel catch. Con strict, il valore catturato è unknown (si può lanciare qualsiasi cosa), e instanceof è il modo per tornare a un errore tipizzato.

Output:

404: /missing.txt
TypeError: path must be absolute

class X extends Error funziona con instanceof su ogni target supportato da TypeScript 7 (ES2015 e successivi). Il vecchio consiglio di chiamare Object.setPrototypeOf(this, X.prototype) nel costruttore valeva per il codice compilato in ES5, un target che TypeScript 7 ha rimosso.

instanceof non funziona con interfacce o tipi

Interfacce e alias di tipo esistono solo per il compilatore. Dopo la compilazione non c'è alcun valore User con cui confrontare, quindi TypeScript rifiuta il controllo:

Il compilatore segnala index.ts(8,24): error TS2693: 'User' only refers to a type, but is being used as a value here. Ci sono due soluzioni. Controlla da te la forma con una type guard, una funzione che restituisce value is User:

Oppure, se è il tuo codice a creare questi oggetti, trasforma User in una classe e costruiscili con new; allora instanceof funziona. La pagina sulle type guard spiega nel dettaglio i predicati e le assertion function.

Il tipo giusto non è l'istanza giusta

TypeScript confronta i tipi in base alla struttura: un oggetto letterale con gli stessi membri di una classe è assegnabile al tipo della classe. instanceof non guarda la struttura. Percorre la catena dei prototipi, e un oggetto mai creato con new non supera il controllo anche quando il compilatore lo accetta come quel tipo.

L'ultima riga è importante. structuredClone, JSON.parse(JSON.stringify(...)) e i messaggi tra worker restituiscono tutti oggetti semplici senza il prototipo della classe, anche se il loro tipo statico può ancora dire Point. Quando istanze di classe attraversano un confine del genere, ricostruiscile (new Point(copy.x, copy.y)) prima di contare su instanceof o sui metodi.

instanceof e primitivi

I primitivi (string, number, boolean...) non sono oggetti e non hanno una catena dei prototipi, quindi "hi" instanceof String è false. TypeScript segnala l'errore quando può: con un valore di tipo string a sinistra, instanceof dà l'errore TS2358, The left-hand side of an 'instanceof' expression must be of type 'any', an object type or a type parameter. Per i primitivi usa typeof.

ValoreControllo instanceofRisultato
new Date()instanceof Datetrue
[1, 2]instanceof Arraytrue (ma meglio Array.isArray)
new TypeError("x")instanceof Errortrue (sottoclasse)
{ x: 1, y: 2 }instanceof Pointfalse (mai costruito)
Object.create(null)instanceof Objectfalse (nessun prototipo)
"hi"instanceof Stringfalse (primitivo)

Valori da altri realm

instanceof confronta con un preciso oggetto costruttore. Il codice eseguito in un altro realm (un iframe, o un contesto vm di Node) ha i propri Array, Error e Date, quindi un array creato lì non supera instanceof Array qui. Lo stesso succede quando due copie dello stesso pacchetto npm finiscono in node_modules: ogni copia ha la sua classe, e un'istanza dell'una non supera instanceof rispetto all'altra. Per gli array, Array.isArray funziona tra realm diversi. Per i tuoi tipi, controllare una proprietà (una type guard, o un campo kind) evita del tutto il problema.

Domande frequenti

Come si controlla se un oggetto è un'istanza di una classe in TypeScript?

Usa value instanceof ClassName. È un controllo a runtime (JavaScript puro), e TypeScript restringe value a ClassName dentro l'if. Funziona sia con le classi integrate come Date, Map ed Error sia con le tue.

Si può usare instanceof con un'interfaccia in TypeScript?

No. Interfacce e alias di tipo vengono cancellati quando TypeScript compila in JavaScript, quindi a runtime non c'è nulla con cui confrontare. x instanceof User con un'interfaccia User dà l'errore TS2693, "'User' only refers to a type, but is being used as a value here." Controlla invece le proprietà con una funzione type guard, oppure trasforma User in una classe se crei tu gli oggetti.

Perché instanceof restituisce false per un oggetto del tipo giusto?

I tipi di TypeScript sono strutturali: un oggetto letterale { x: 1, y: 2 } è assegnabile al tipo di una classe Point se ha gli stessi membri. Ma instanceof controlla la catena dei prototipi, e il letterale non è mai stato creato con new Point, quindi il risultato è false. Lo stesso succede alle istanze di classe passate per JSON, structuredClone o un canale di messaggi, che tornano indietro come oggetti semplici.

instanceof funziona con classi Error personalizzate in TypeScript?

Sì, con qualsiasi target moderno. class NotFound extends Error {} e poi err instanceof NotFound è true. Il vecchio problema per cui restituiva false riguardava solo l'output compilato in ES5, e TypeScript 7 non supporta più il target ES5.

Perché "hello" instanceof String è false?

Una stringa letterale è un primitivo, non un oggetto, quindi non ha una catena dei prototipi da controllare. instanceof String è vero solo per gli oggetti wrapper creati con new String(). TypeScript rifiuta instanceof su un valore di tipo string (TS2358); per i primitivi usa typeof value === "string".

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA