Власні класи помилок дають змогу розрізняти помилки за типом, а не за текстом повідомлення, і передавати додаткові дані.
class HttpError extends Error {
constructor(response, options) {
super(`HTTP ${response.status} для ${response.url}`, options);
this.name = 'HttpError';
this.status = response.status;
}
}
class ValidationError extends Error {
constructor(errors) {
super('Дані не пройшли перевірку');
this.name = 'ValidationError';
this.errors = errors; // { email: ['Поле обов'язкове'] }
}
}
this.name варто задавати явно: саме воно відображається в консолі й стеку (ValidationError: Дані не пройшли перевірку). Без нього всі помилки виглядатимуть як Error.
Обробка за типом:
async function request(url, options) {
const response = await fetch(url, options);
if (response.status === 422) {
throw new ValidationError((await response.json()).errors);
}
if (!response.ok) {
throw new HttpError(response);
}
return response.json();
}
try {
await request('/api/orders', { method: 'POST', body });
} catch (error) {
if (error instanceof ValidationError) {
showFieldErrors(error.errors); // формат помилок валідації Laravel
} else if (error instanceof HttpError && error.status === 403) {
showError('Недостатньо прав');
} else {
throw error;
}
}
cause (ES2022) - посилання на початкову помилку, що спричинила поточну. Передається другим аргументом конструктора:
try {
config = JSON.parse(await readConfig());
} catch (error) {
throw new Error('Не вдалося завантажити конфігурацію', { cause: error });
}
Навіщо: на вищому рівні потрібне зрозуміле повідомлення («не вдалося завантажити конфігурацію»), але без втрати технічної причини («Unexpected token } in JSON at position 42»). Раніше доводилося або губити початкову помилку, або склеювати повідомлення рядками. Тепер ланцюжок причин зберігається повністю, і DevTools та Sentry показують його.
Це аналог previous у винятках PHP (new RuntimeException('...', previous: $e)).
Що варто знати:
instanceofпрацює з класами, що наслідуютьError, - у сучасних середовищах без хитрощів (раніше Babel ламав це при компіляції в ES5);- не варто створювати клас на кожну дрібницю - лише там, де код по-різному реагує на помилки;
- помилки, що перетинають межі (з воркера, з
postMessage, з серверу), втрачають клас - при серіалізації залишаються лишеnameіmessage. Тип доведеться відновлювати заnameчи кодом.