DanLevy.net

Quiz: Padronanza Avanzata degli Errori JS

Le tue eccezioni sono davvero eccezionali?

Pensi di conoscere a fondo gli errori JavaScript?

Cosa restituisce JSON.stringify(error)?

const error = new Error('Oops');
console.log(JSON.stringify(error));

Gli oggetti Error hanno proprietà non enumerabili (message, name, stack), quindi JSON.stringify() restituisce {}. Questo è un inconveniente comune quando si inviano errori nelle risposte API. Usa JSON.stringify(error, Object.getOwnPropertyNames(error)) o crea invece un oggetto semplice.

Qual è la differenza tra questi due?

const err = new Error('Test');
console.log(err);
console.log(JSON.stringify(err));

console.log(err) mostra l’errore con il suo messaggio e lo stack trace perché la console ha una gestione speciale per gli oggetti Error. JSON.stringify(err) restituisce '{}' perché le proprietà di Error non sono enumerabili. Questa differenza mette in difficoltà molti sviluppatori che debugano le API.

Quali sono i risultati di questi controlli?

class CustomError extends Error {}
const err = new CustomError('test');
console.log(err instanceof CustomError);
console.log(err instanceof Error);
console.log(err instanceof Object);

Tutte e tre restituiscono true. CustomError estende Error, che estende Object. L’operatore instanceof controlla l’intera catena di prototipi, quindi un’istanza di CustomError è anche un’istanza di Error e Object.

Cosa succede con instanceof Error tra iframe?

// In iframe:
const iframeError = new Error('test');
// In parent window:
console.log(iframeError instanceof Error);

instanceof può restituire false tra diversi contesti di esecuzione (iframe, worker) perché ogni contesto ha il proprio costruttore Error. Usa Object.prototype.toString.call(obj) === '[object Error]' per una rilevazione affidabile degli errori tra contesti.

Cosa succede quando lanci una stringa?

try {
throw "Oops!";
} catch (e) {
console.log(e instanceof Error);
console.log(typeof e);
}

JavaScript consente di lanciare qualsiasi valore. Qui, e instanceof Error è false e typeof e è "string". Questo può rompere il codice di gestione degli errori che presume che tutte le eccezioni catturate siano oggetti Error. Lancia sempre istanze di Error per un debug migliore.

Qual è il valore di err.name?

class CustomError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
const err = new CustomError('test');
console.log(err.name);

err.name è "CustomError" perché this.constructor.name restituisce il nome della classe. Impostare this.name = this.constructor.name è un pattern comune per garantire che le classi di errore personalizzate mostrino il nome corretto nelle tracce di stack e nei messaggi di errore.

Qual è l’output senza impostare name?

class MyError extends Error {
// No constructor or name setting
}
const err = new MyError('test');
console.log(err.name);

Se non imposti esplicitamente this.name, l’errore eredita la proprietà name predefinita dalla classe Error, che è "Error". Per questo le classi di errore personalizzate dovrebbero sempre impostare this.name = this.constructor.name nel loro costruttore.

Cosa restituisce wrapper.cause.message?

const original = new Error('Original error');
const wrapper = new Error('Wrapper',
{ cause: original }
);
console.log(wrapper.cause.message);

Error.cause (ES2022) consente di concatenare errori per preservare il contesto dell’errore originale. wrapper.cause fa riferimento all’errore originale, quindi wrapper.cause.message restituisce "Original error". Questo è utile per avvolgere errori di livello inferiore con un contesto di livello superiore.

Cosa fa Error.captureStackTrace?

function createError(msg) {
const err = new Error(msg);
Error.captureStackTrace(err, createError);
return err;
}
const error = createError('test');

Error.captureStackTrace (V8/Node.js) rimuove la funzione specificata (createError) dalla traccia dello stack, rendendo le funzioni factory di errore invisibili agli utenti finali. Questo genera tracce dello stack più pulite che indicano il punto in cui la factory è stata chiamata, non la factory stessa.

Qual è il messaggio di errore?

function validate(value) {
if (!value) {
throw new Error(
`Value ${value} is invalid`
);
}
}
try {
validate(undefined);
} catch (e) {
console.log(e.message);
}

I letterali template convertono undefined nella stringa "undefined" durante l’interpolazione. Il messaggio di errore diventa "Value undefined is invalid". Per messaggi più puliti, considera di usare value ?? 'null' o controlli simili prima dell’interpolazione.

Cosa viene inviato al client?

// Express.js route
app.get('/api/data', (req, res) => {
const error = new Error('Database failed');
res.json({ error });
});

res.json() usa internamente JSON.stringify(), quindi l’oggetto Error diventa {}. Il client riceve {"error":{}}. Per risolvere, usa res.json({ error: error.message }) oppure res.json({ error: { message: error.message, name: error.name } }).

Cosa può accettare Promise.reject()?

Promise.reject('string').catch(e =>
console.log(typeof e)
);
Promise.reject({code: 404}).catch(e =>
console.log(e.code)
);
Promise.reject(42).catch(e =>
console.log(e)
);

Come throw, Promise.reject() accetta qualsiasi valore – stringhe, oggetti, numeri, ecc. Questo stampa "string", 404 e 42. Controlla sempre il tipo dei valori catturati nelle catene di promise, soprattutto quando lavori con codice di terze parti che potrebbe rifiutare con valori non‑Error.

Quanto sono affidabili error.code e error.errno?

const fs = require('fs');
fs.readFile('missing.txt', (err, data) => {
if (err) {
console.log(err.code); // 'ENOENT'
console.log(err.errno); // -2
}
});

Proprietà come code e errno sono specifiche dell’ambiente (Node.js in questo caso) e non fanno parte dell’oggetto Error standard. Gli errori del browser non avranno queste proprietà. Controlla sempre la loro esistenza: if (err.code === 'ENOENT') invece di presumere che esistano.

Cosa restituiscono questi controlli?

const fakeError = {
name: 'Error',
message: 'Fake error',
stack: 'fake stack'
};
console.log(fakeError instanceof Error);
console.log(Object.prototype.toString.call(
fakeError
) === '[object Error]');

instanceof Error restituisce false perché l’oggetto non è stato creato dal costruttore Error. Object.prototype.toString.call() restituisce anch’esso false (restituisce '[object Object]') perché controlla lo slot interno [[Class]]. Entrambi i metodi identificano correttamente questo come un oggetto errore falsificato.

Padroneggia l’Arte della Gestione degli Errori

Dalle trappole della serializzazione ai fallimenti di instanceof tra contesti diversi, questi concetti avanzati distinguono i junior dai professionisti davvero provati.

Pronto per altre sfide? Dai un’occhiata alla nostra raccolta completa di quiz per altri rompicapi su JavaScript, algoritmi e molto altro!