Storia della questione: In JavaScript, gli errori standard sono descritti tramite la classe Error. In grandi progetti TypeScript, è importante utilizzare la propria gerarchia di errori per una gestione precisa delle situazioni, ma ci sono delle sfide relative ai tipi, all'ereditarietà e al comportamento di 'instanceof'.
Problema: Erediterando da Error possono sorgere difficoltà: si perde il prototipo, 'instanceof' non funziona correttamente, i tipi possono essere descritti in modo errato, la serializzazione porta spesso alla perdita dello stack trace. Senza specificare esplicitamente le proprietà, si possono verificare bug nella gestione degli errori.
Soluzione: Definire correttamente gli errori personalizzati estendendo la classe Error. È necessario specificare esplicitamente il nome, ripristinare manualmente il prototipo (importante per ES5 e durante la compilazione in CommonJS), e tipizzare i campi degli errori.
Esempio di codice:
class ValidationError extends Error { code: number; constructor(message: string, code: number = 400) { super(message); Object.setPrototypeOf(this, ValidationError.prototype); this.name = 'ValidationError'; this.code = code; } } function process(user: string) { if (!user) throw new ValidationError('User required', 401); }
Caratteristiche chiave:
Perché non si può omettere la chiamata a Object.setPrototypeOf(this, ...)?
Se non si chiama Object.setPrototypeOf(this, Class.prototype), 'instanceof' ValidationError non funzionerà quando compilato in ES5/CommonJS o Babel. Questo porterà al fatto che i blocchi catch di ValidationError non cattureranno l'errore.
class CustomErr extends Error {} const err = new CustomErr('msg'); console.log(err instanceof CustomErr); // false senza setPrototypeOf
Si può omettere il campo name nell'errore personalizzato?
Se non si imposta la proprietà this.name, lo stack dell'errore e la visualizzazione nei log saranno errati, rendendo difficile la ricerca delle cause e la classificazione degli errori.
È necessario rendere gli errori serializzabili, e se sì, come?
Gli errori devono essere correttamente serializzati (ad esempio, per il logging o la trasmissione in rete), altrimenti JSON.stringify(new Error()) non restituirà message e stack. È necessario sovrascrivere il metodo toJSON.
class SerializableError extends Error { toJSON() { return { name: this.name, message: this.message, stack: this.stack }; } }
Nel progetto è stato realizzato semplicemente class MyError extends Error, senza ripristinare il prototipo. Gli errori venivano catturati tramite if (err instanceof MyError), ma questo non funzionava, e il codice ignorava silenziosamente la gestione delle situazioni critiche.
Vantaggi:
Svantaggi:
È stato implementato correttamente CustomError, specificando esplicitamente name, code e toJSON, con test che coprivano la gestione di diversi tipi di errori. Nei log e nei gestori catch la struttura degli errori è diventata chiara, il che ha ridotto il tempo necessario a trovare bug.
Vantaggi:
Svantaggi: