Custom Errors: создание собственных типов ошибок
Зачем нужны свои ошибки? 🤔
Стандартные ошибки JavaScript (Error, SyntaxError, TypeError) — это здорово, но иногда их не хватает. Представь, что ты пишешь библиотеку для работы с платежами. Какой смысл использовать общие ошибки, если можно создать свою — PaymentError?
Свои ошибки делают код:
- Понятнее – сразу ясно, что пошло не так
- Удобнее для отладки – можно добавить специфичные свойства
- Профессиональнее – это уровень senior-разработчика!
// Вместо этого:
throw new Error('Недостаточно средств')
// Делаем так:
throw new PaymentError('Недостаточно средств', { userId: 123, amount: 500 })
Базовый пример: создаём класс ошибки 🏗️
Создадим простейшую кастомную ошибку, наследуясь от Error:
class ValidationError extends Error {
constructor(message) {
super(message) // передаём message в родительский класс
this.name = 'ValidationError' // имя ошибки
}
}
// Используем
try {
throw new ValidationError('Некорректный email')
} catch (err) {
console.log(err.name) // ValidationError
console.log(err.message) // Некорректный email
console.log(err.stack) // стек вызовов сохраняется!
}
💡 Всегда задавай
this.name— это важно для отладки! Без него вerr.nameбудет просто"Error".
Расширяем функционал: добавляем контекст 🔍
Главная фишка кастомных ошибок — возможность добавлять любые дополнительные данные. Допустим, мы валидируем форму:
class FormValidationError extends Error {
constructor(message, field, value) {
super(message)
this.name = 'FormValidationError'
this.field = field // какое поле не прошло валидацию
this.value = value // какое значение было передано
}
}
// Пример использования
function validateAge(age) {
if (age < 18) {
throw new FormValidationError('Возраст меньше 18', 'age', age)
}
}
try {
validateAge(15)
} catch (err) {
if (err instanceof FormValidationError) {
console.error(`Ошибка в поле ${err.field}: ${err.message}`)
console.error(`Переданное значение: ${err.value}`)
}
}
Проверка типа ошибки: instanceof vs err.name 🔎
Есть два способа проверить тип ошибки:
try {
// какой-то код
} catch (err) {
// Способ 1 (предпочтительный!)
if (err instanceof ValidationError) {
// обрабатываем именно ValidationError
}
// Способ 2
if (err.name === 'ValidationError') {
// тоже работает, но менее надёжно
}
}
⚠️
instanceof— более надёжный вариант, особенно если учитывать наследование и кросс-оконные сценарии (например, iframe'ы).
Продвинутый пример: иерархия ошибок 🏰
Настоящая магия начинается, когда ты строишь целую систему ошибок. Вот как может выглядеть структура для API:
// Базовый класс для всех API-ошибок
class ApiError extends Error {
constructor(message, statusCode) {
super(message)
this.name = this.constructor.name
this.statusCode = statusCode || 500
}
}
// Ошибка "Не найдено"
class NotFoundError extends ApiError {
constructor(resource) {
super(`Ресурс ${resource} не найден`, 404)
this.resource = resource
}
}
// Ошибка авторизации
class UnauthorizedError extends ApiError {
constructor(message = 'Требуется авторизация') {
super(message, 401)
}
}
// Использование
fetch('/user/123')
.then(response => {
if (!response.ok) {
throw new NotFoundError('Пользователь')
}
})
.catch(err => {
if (err instanceof NotFoundError) {
showToast(`Ошибка: ${err.message}`)
}
})
Лучшие практики 💎
- Наследуйся от Error – это даст стек вызовов и все стандартные свойства
- Используй
this.name– иначе теряется смысл кастомной ошибки - Добавляй контекст – любые дополнительные данные для отладки
- Создавай иерархию – базовый класс ошибки для приложения + наследники
- Логируй разумно – не забывай про
error.stackв production
// Идеальный пример
class DatabaseError extends Error {
constructor(message, query, params) {
super(message)
this.name = 'DatabaseError'
this.query = query // проблемный SQL-запрос
this.params = params // параметры запроса
this.timestamp = new Date()
}
}
Когда не стоит создавать свои ошибки 🚧
Не нужно изобретать велосипед, если:
- Ошибка совсем общая (лучше использовать
new Error()) - Ты пишешь маленькую утилиту без сложной логики
- В команде нет конвенции по именованию ошибок
📺 Подробнее о тонкостях работы с ошибками смотри в курсе Данилы Бежина: https://www.youtube.com/@DanilaBezhin (разбор реальных кейсов из production!)