Gli errori sono oggetti con un tipo
Quando JavaScript lancia un errore, non ti restituisce una semplice stringa: ti restituisce un oggetto. Quell'oggetto ha un tipo (il suo costruttore) e alcune proprietà standard: name, message e stack.
err.name è un'etichetta breve come "TypeError". err.message è la descrizione leggibile. err instanceof TypeError ti dice la classe specifica. Conoscere il tipo è importante: ti dice se il problema è un refuso, un valore sbagliato o codice che non è stato nemmeno interpretato.
Esistono sette tipi di errore predefiniti. Tre li vedrai di continuo, quattro solo ogni tanto.
SyntaxError: il codice non è stato interpretato
Un SyntaxError significa che JavaScript non è riuscito nemmeno a leggere il tuo codice. Non è davvero un errore di runtime: il motore fallisce durante il parsing, prima che venga eseguita una sola riga. Non puoi intercettare con try/catch un SyntaxError nello stesso file, perché l'intero file viene rifiutato.
function greet(name {
return "hi, " + name;
}
// SyntaxError: Unexpected token '{'
Una parentesi mancante, una virgola di troppo, un return fuori da una funzione: qualsiasi cosa rompa la grammatica lancia questo errore. La soluzione è sempre correggere il sorgente. L'unico caso in cui puoi catturare un SyntaxError è quando il parsing avviene a runtime, come con JSON.parse:
JSON.parse riceve una stringa durante l'esecuzione, quindi i suoi errori di sintassi si possono catturare. Quelli nei tuoi file sorgente no.
ReferenceError: quel nome non esiste
Un ReferenceError scatta quando fai riferimento a una variabile che non è stata dichiarata in nessuno scope visibile dal codice corrente.
Nove volte su dieci si tratta di un refuso (totl invece di total). Il resto dei casi riguarda lo scope: stai cercando di usare qualcosa dichiarato in un'altra funzione o in un altro modulo.
C'è poi una causa più sottile: la temporal dead zone. Le dichiarazioni let e const esistono dall'inizio del loro blocco, ma non puoi accedervi prima della riga che le dichiara:
Quando viene eseguito console.log(x), x è già un binding reale, ma non è ancora stato inizializzato. Da qui il reference error. La soluzione è spostare l'accesso dopo la dichiarazione.
TypeError: il valore aveva la forma sbagliata
Un TypeError significa che un valore esiste, ma non è del genere che l'operazione si aspetta. Chiamare qualcosa che non è una funzione, leggere una proprietà di null o undefined, assegnare un nuovo valore a una const: sono tutti TypeError.
"Cannot read properties of null (reading 'name')" è probabilmente il messaggio di errore più comune in assoluto in JavaScript. La soluzione è garantire che il valore esista, oppure proteggere l'accesso con l'optional chaining: user?.name.
Altre varianti di TypeError:
Chiamare un numero, riassegnare una const, chiamare un metodo che non esiste: il valore era del tipo sbagliato per quello che gli hai chiesto di fare.
RangeError: il numero è fuori dai limiti
RangeError scatta quando un numero è tecnicamente valido ma fuori dall'intervallo consentito per una specifica operazione.
La causa classica è la ricorsione infinita, che fa saltare lo stack delle chiamate:
"Maximum call stack size exceeded" significa quasi sempre che una funzione chiama sé stessa senza un caso base, oppure che due funzioni si chiamano a vicenda in un ciclo.
URIError ed EvalError: quelli rari
URIError arriva dalle funzioni di gestione degli URI (encodeURI, decodeURIComponent e simili) quando ricevono un input malformato:
EvalError è un residuo del passato. I motori JavaScript moderni non lo lanciano più per nessun motivo: esiste ancora come costruttore per retrocompatibilità. Puoi crearne uno a mano, ma non lo vedrai mai nel codice reale.
La catena di ereditarietà
Tutti questi tipi ereditano da una classe base Error. Questo significa che err instanceof Error è true per ognuno di loro, cosa comoda in un catch generico:
Un blocco catch cattura tutto, compresi i valori che non sono errori se qualcuno scrive throw "oops". Per questo l'ultimo ramo è importante. Restringi sempre con instanceof prima di trattare un valore catturato come un oggetto errore.
Lanciare errori di proposito
Puoi lanciare tu stesso uno qualsiasi dei tipi predefiniti quando il tuo codice rileva un problema. Scegli il tipo che corrisponde al fallimento:
Far corrispondere il tipo al fallimento non è solo una questione estetica: permette a chi chiama la funzione di scrivere una logica catch mirata invece di analizzare i messaggi di errore.
Classi di errore personalizzate
Quando i tipi predefiniti non vanno bene, estendi Error:
Due cose da ricordare: chiama super(message) perché la classe base Error venga inizializzata correttamente, e imposta this.name perché i log mostrino l'etichetta giusta. Campi personalizzati come field permettono a chi chiama di reagire a specifici tipi di errore senza dover analizzare stringhe.
Prossimo passo: console e DevTools
Conoscere i tipi di errore è metà del lavoro. L'altra metà è saper leggere uno stack trace e ispezionare lo stato mentre il programma è in esecuzione. Gli strumenti per sviluppatori del browser (e il debugger di Node) trasformano "ha lanciato un errore e non so perché" in pochi secondi di ispezione. È il prossimo argomento.
Domande frequenti
Quali sono i tipi di errore predefiniti in JavaScript?
JavaScript ne ha sette: Error (la classe base), SyntaxError, ReferenceError, TypeError, RangeError, URIError e EvalError. Ognuno è un costruttore che produce un oggetto errore con le proprietà name, message e stack. Quelli che incontrerai più spesso sono SyntaxError, ReferenceError e TypeError.
Che differenza c'è tra un SyntaxError e un TypeError?
Un SyntaxError significa che il codice non è JavaScript valido: il motore non riesce nemmeno a interpretarlo. Un TypeError significa che il codice è stato interpretato correttamente, ma durante l'esecuzione ha fatto qualcosa di non consentito, come chiamare qualcosa che non è una funzione o leggere una proprietà di null. Gli errori di sintassi bloccano l'intero script; i TypeError scattano solo quando viene eseguita la riga sbagliata.
Quando ottengo un ReferenceError in JavaScript?
Quando usi un nome che non è stato dichiarato, oppure accedi a una variabile let/const nella sua temporal dead zone (prima che venga eseguita la dichiarazione). La causa più comune sono i refusi: consoel.log(x) lancia ReferenceError: consoel is not defined. Controlla prima l'ortografia e lo scope.
Posso creare i miei tipi di errore?
Sì. Estendi la classe predefinita Error: class ValidationError extends Error { }. Imposta this.name nel costruttore, così i log e i rami catch possono distinguerlo. Le classi di errore personalizzate ripagano quando errori diversi richiedono una gestione diversa.