Un type guard è un controllo a runtime che TypeScript capisce, quindi restringe il tipo dentro il ramo controllato. typeof, instanceof e in sono guard integrati; per tutto il resto scrivi una funzione il cui tipo di ritorno è un type predicate, value is Type.
A runtime isUser restituisce un semplice booleano. Il tipo di ritorno value is User dice al compilatore cosa dimostra un risultato true, e ogni if (isUser(x)) restringe quindi x a User.
Type guard integrati
Questi controlli restringono il tipo senza alcuna funzione di supporto:
| Guard | Esempio | Usalo per |
|---|---|---|
typeof | typeof x === "number" | primitivi e funzioni |
instanceof | x instanceof Date | istanze di classi |
in | "email" in x | union di oggetti, proprietà di oggetti unknown |
Array.isArray | Array.isArray(x) | array |
| Uguaglianza | x === null, x.kind === "circle" | null/undefined, etichette letterali |
| Truthiness | if (x) | eliminare null e undefined |
Vengono tutti eseguiti come normale JavaScript. Ciò che TypeScript aggiunge è il restringimento: legge il controllo e adegua il tipo in ogni ramo. L'elenco completo delle forme è nella pagina sul type narrowing. Un guard personalizzato serve per i controlli che non stanno in una sola espressione, o che vuoi riutilizzare.
Scrivere un type predicate
Un type predicate ha la forma parameterName is Type e sostituisce boolean come tipo di ritorno. Il restringimento funziona in entrambe le direzioni: true restringe a Type, e false toglie Type da una union.
Passare un guard a filter produce un array tipizzato correttamente. Da TypeScript 5.5 il compilatore deduce un predicato anche dalle arrow function semplici, quindi pets.filter((p) => p.kind === "cat") restituisce Cat[] senza un guard con nome.
Il tipo del predicato deve essere compatibile con quello del parametro: function f(x: string): x is number è l'errore TS2677, A type predicate's type must be assignable to its parameter's type.
Il compilatore si fida del tuo guard
TypeScript controlla che un guard restituisca un booleano. Non controlla che il booleano sia giusto. Un guard che restituisce true per i valori sbagliati fa mentire i tipi, e il programma fallisce a runtime senza alcun errore di compilazione.
data.price.toFixed(2) lancia TypeError: Cannot read properties of undefined (reading 'toFixed') a runtime. Il compilatore ha accettato data.price come number perché lo diceva il guard. Controlla ogni proprietà su cui si basa il resto del codice, e mantieni i guard piccoli, testati e vicini al tipo che descrivono.
Verificare se un oggetto è di un certo tipo
È la domanda dietro la maggior parte dei guard personalizzati: i dati arrivano come unknown (da JSON.parse, fetch, localStorage, un messaggio) e devi sapere se corrispondono alla tua interface. La ricetta:
typeof value === "object" && value !== null(un oggetto, nonnull)."prop" in valueper ogni proprietà obbligatoria. Suunknown,inaggiunge la proprietà al tipo comeunknown.typeof value.prop === "..."(o un guard annidato) per il tipo di ogni proprietà.Array.isArray(value.items) && value.items.every(isItem)per gli array.
Per forme grandi o molto annidate, scrivere questi controlli a mano diventa noioso. Librerie di schema come Zod o Valibot ti permettono di descrivere la forma una sola volta e ti danno sia il controllo a runtime sia il tipo TypeScript.
Assertion function: asserts value is Type
Una assertion function lancia un'eccezione se il controllo fallisce e altrimenti termina normalmente. Il suo tipo di ritorno è asserts value is Type (o asserts condition), e tutto ciò che viene dopo la chiamata è ristretto, senza bisogno di un if.
Una regola trae spesso in inganno: una assertion function va chiamata tramite un nome con un tipo esplicito. Una arrow function const senza annotazione, const check = (v: unknown): asserts v is string => {...}, dà l'errore TS2775 nel punto della chiamata, Assertions require every name in the call target to be declared with an explicit type annotation. Usa una dichiarazione function, oppure annota la costante con un tipo funzione.
Guard, asserzioni e cast a confronto
| Strumento | Controllo a runtime? | Restringe | Se fallisce |
|---|---|---|---|
Guard integrato (typeof, in...) | Sì | dentro il ramo | prende l'altro ramo |
Funzione value is T | Sì (il tuo codice) | dentro il ramo | prende l'altro ramo |
Funzione asserts value is T | Sì (il tuo codice) | dopo la chiamata | lancia un'eccezione |
value as T | No | l'espressione | niente: il tipo sbagliato si propaga |
Una type assertion (as) cambia il tipo senza controllare nulla. Al confine in cui i dati arrivano da fuori, un guard o una assertion function è la versione sicura della stessa idea.
Guard basati su this nelle classi
Un metodo può restringere l'oggetto su cui viene chiamato con this is Type. È comodo nelle gerarchie di classi:
class FileNode {
constructor(public name: string) {}
isDirectory(): this is DirectoryNode {
return this instanceof DirectoryNode;
}
}
class DirectoryNode extends FileNode {
children: FileNode[] = [];
}
function count(node: FileNode): number {
return node.isDirectory() ? node.children.length : 0; // node: DirectoryNode in the true branch
}
Domande frequenti
Cos'è un type guard in TypeScript?
Qualsiasi controllo a runtime che TypeScript usa per restringere un tipo: typeof x === "string", x instanceof Date, "id" in x, Array.isArray(x), oppure la chiamata a una funzione il cui tipo di ritorno è un type predicate come x is User. Dentro il ramo controllato la variabile ha il tipo più stretto.
Come verifico se un oggetto è di un certo tipo in TypeScript?
I tipi non esistono a runtime, quindi controlli le proprietà: scrivi una funzione isUser(value: unknown): value is User che verifica typeof value === "object", value !== null e ogni proprietà obbligatoria con in e typeof. Dopo if (isUser(x)), x ha tipo User. Per le classi basta x instanceof MyClass.
Cosa significa "value is Type" in TypeScript?
È un type predicate, usato come tipo di ritorno di una funzione. A runtime la funzione restituisce comunque un booleano, ma quando restituisce true TypeScript restringe l'argomento a Type nel punto della chiamata, e quando restituisce false lo restringe agli altri membri della union. Il compilatore non verifica il corpo della funzione, quindi il controllo deve essere corretto.
Che differenza c'è tra un type guard e una assertion function?
Un type guard (x is T) restituisce un booleano e restringe il tipo dentro un if. Una assertion function (asserts x is T) non restituisce nulla e lancia un'eccezione quando il controllo fallisce, quindi tutto ciò che segue la chiamata è ristretto senza bisogno di un if. Usa i guard per le diramazioni e le asserzioni per "questo deve valere, altrimenti fermati".
Posso verificare se un oggetto implementa un'interface in TypeScript?
Non direttamente: le interface vengono eliminate e instanceof non le accetta. Scrivi un type guard che controlla le proprietà dell'interface, oppure aggiungi una proprietà etichetta letterale (kind: "user") e confrontala. Librerie di schema come Zod generano sia il controllo sia il tipo da un'unica definizione.