Errori personalizzati in JavaScript
JavaScript consente di creare tipi di errore personalizzati estendendo la classe Error integrata, per una gestione degli errori più dettagliata.
Errori personalizzati in JavaScript
JavaScript consente di creare tipi di errore personalizzati estendendo la classe Error integrata. Un errore personalizzato è semplicemente una classe che eredita da Error (o da un altro tipo di errore) e porta un name significativo più eventuali dati aggiuntivi richiesti dalla situazione.
Questa pagina tratta il perché si vogliono usare errori personalizzati, come definirli correttamente, come distinguere i tipi di errore con instanceof, come allegare contesto (codici di stato, nomi di campo, la cause originale) e come organizzare una gerarchia di errori per un'applicazione reale.
Perché creare errori personalizzati?
Lanciare un semplice new Error("...") funziona, ma fornisce al chiamante solo una stringa da esaminare. Gli errori personalizzati risolvono tre problemi:
- Gestione basata sul tipo. Con
instanceof ValidationErrorsi può reagire a un tipo specifico di fallimento senza analizzare il testo del messaggio, che è fragile e dipendente dalla lingua. - Contesto aggiuntivo. Una classe personalizzata può portare campi strutturati — un
statusCodeHTTP, ilfieldincriminato, un suggerimento per il nuovo tentativo — invece di concentrare tutto nel messaggio. - Gerarchie chiare. Una classe base condivisa (ad esempio
AppError) consente a un gestore di primo livello di intercettare tutti gli errori dell'applicazione con un singoloinstanceof, pur permettendo al codice interno di lanciare sottotipi specifici.
Se si ha bisogno di imparare prima i meccanismi di lancio/cattura, leggere Gestione degli errori: try...catch. Gli errori personalizzati si basano direttamente sull'ereditarietà delle classi e sull'estensione delle classi integrate.
Nozioni di base sull'estensione della classe Error
Per creare un errore personalizzato, si usa extend sulla classe Error. La nuova classe eredita message, stack e toString() di Error, e si aggiunge tutto ciò che serve.
Creazione di una classe di errore personalizzata
Ecco il pattern minimale:
class ValidationError extends Error {
constructor(message) {
super(message); // Pass the message to the Error constructor (sets this.message)
this.name = "ValidationError"; // Override the default name "Error"
}
}Due dettagli sono importanti:
super(message)deve venire prima. All'interno del costruttore di una sottoclasse non è possibile usarethisprima di chiamaresuper(). Il costruttore diErrorimpostathis.messagee cattura lo stack trace.- Impostare
this.name. Senza di esso, l'errore viene riportato come"Error"nei messaggi e intoString(). Impostarenamerende leggibili i log e il patternswitch (error.name).
In alcuni esempi si può vedere anche Object.setPrototypeOf(this, new.target.prototype). Con i transpiler moderni e le classi native è di solito non necessario, ma non causa danni e garantisce che instanceof funzioni anche quando il codice viene compilato in ES5 o condiviso tra realm (iframe/worker boundary). Gli esempi qui sotto lo mantengono per sicurezza.
Una volta creato, l'errore si comporta come qualsiasi altro:
const e = new ValidationError("bad input");
e.name; // "ValidationError"
e instanceof Error; // true — it is still a real Error
e.toString(); // "ValidationError: bad input"Utilizzo degli errori personalizzati
Una volta definito un errore personalizzato, è possibile lanciarlo nell'applicazione come qualsiasi errore standard:
In questo esempio, un'email viene convalidata rispetto a un'espressione regolare. Se la validazione fallisce, viene lanciato un ValidationError. Questo errore viene poi catturato nel blocco try...catch e, se è un'istanza di ValidationError, viene registrato un messaggio specifico.
Gestione di più tipi di errori personalizzati
Potrebbe essere necessario creare vari tipi di errori per diverse parti dell'applicazione. Ecco come gestire più errori personalizzati:
Questa impostazione consente una gestione distinta dei diversi tipi di errore, rendendo l'applicazione più robusta e facile da eseguire il debug.
Preferire instanceof al confronto delle stringhe error.name quando possibile: instanceof corrisponde anche alle sottoclassi, quindi è più resistente al refactoring.
Costruire una gerarchia di errori con una classe base
Nelle applicazioni reali conviene dare a tutti gli errori un antenato comune. Un gestore di primo livello può quindi intercettare ogni errore "atteso" dell'applicazione in un unico punto, pur permettendo al codice più profondo di lanciare sottotipi precisi. L'opzione cause (ES2022) consente a un errore di livello superiore di avvolgere quello di livello inferiore che lo ha causato, preservando l'originale per il debug.
Qui this.name = this.constructor.name elimina la necessità di ripetere il nome in ogni sottoclasse, e il singolo controllo instanceof AppError cattura NotFoundError, ConflictError e qualsiasi futuro sottotipo.
Gestione avanzata degli errori personalizzati in JavaScript
Ampliando le nozioni di base, possiamo applicare gli errori personalizzati a scenari più complessi come le operazioni asincrone e i casi di logica di business specifici.
Esempio 1: Gestione personalizzata degli errori API
Questo esempio mostra come creare e utilizzare un errore personalizzato per gestire i problemi delle richieste API, ad esempio quando una risorsa richiesta non viene trovata o il server restituisce un errore. Il flusso async/await qui si abbina alla gestione degli errori con le promise.
Spiegazione
- Classe ApiError: Questa classe personalizzata cattura gli errori specifici delle API, memorizzando il codice di stato HTTP insieme a un messaggio personalizzato.
- Funzione fetchData: Tenta di recuperare dati da un URL fornito. Se la risposta non ha successo, lancia un
ApiErrorcon un messaggio dettagliato e il codice di stato. - Gestione degli errori: Gli errori vengono catturati e gestiti in modo appropriato. Gli errori relativi alle API vengono registrati con informazioni dettagliate, mentre anche gli errori imprevisti vengono catturati e registrati.
Esempio 2: Gestione personalizzata degli errori di validazione
Utilizzare questo esempio per gestire gli errori relativi alla validazione dei dati, ad esempio il controllo dell'input dell'utente.
Spiegazione
- Classe ValidationError: Una classe di errore personalizzata che aiuta a identificare quale campo specifico dei dati di input non ha superato il controllo di validazione.
- Funzione validateUser: Verifica la validità dei dati dell'utente. Se i dati non soddisfano determinati criteri, lancia un
ValidationError. - Gestione degli errori: Cattura gli errori di validazione e li registra con informazioni dettagliate. Anche gli altri tipi di errori vengono gestiti separatamente.
Best practice e insidie comuni
- Chiamare sempre
super(message)prima, prima di toccarethis. - Impostare
nameaffinché i log etoString()siano leggibili;this.name = this.constructor.namelo fa una volta per tutta una gerarchia. - Preferire
instanceofai confronti conerror.name— corrisponde anche alle sottoclassi. - Rilancia ciò che non riconosci. Un
catchche inghiotte tutto (catch (e) {}) nasconde bug reali. Gestisci i tipi noti e usathrow errorper il resto, come mostrano gli esempi. - Usare
causeper avvolgere, non sostituire. Quando si cattura un errore di basso livello e se ne lancia uno di livello superiore, passare{ cause: original }affinché lo stack trace e la causa radice sopravvivano. - Non usare sottoclassi di
Errorper controllare il flusso. Gli errori sono per condizioni eccezionali, non per la normale ramificazione del codice.
Conclusione
Creare classi di errore personalizzate in JavaScript estendendo la classe Error è una tecnica potente per gestire tipi specifici di errori in modo più granulare. Migliora la chiarezza e la manutenibilità della gestione degli errori nel codice, consentendo di fornire feedback e azioni più specifici in base alle diverse condizioni di errore. Questo metodo non solo aiuta nel debug, ma migliora anche l'affidabilità delle applicazioni assicurando che ogni tipo di errore venga catturato e gestito in modo appropriato.