Типы ошибок и кастомные классы
Встроенные типы ошибок
// TypeError — неверный тип операции
null.property // TypeError: Cannot read properties of null
undefined() // TypeError: undefined is not a function
42 + Symbol() // TypeError: Cannot convert Symbol to number
// RangeError — значение вне допустимого диапазона
new Array(-1) // RangeError: Invalid array length
(1.23456789).toFixed(200) // RangeError: toFixed() digits argument must be...
// ReferenceError — обращение к несуществующей переменной
console.log(undeclared) // ReferenceError: undeclared is not defined
// SyntaxError — ошибка синтаксиса (чаще при парсинге)
JSON.parse('{bad json}') // SyntaxError: Unexpected token
// URIError — ошибка URI
decodeURIComponent('%') // URIError: URI malformed
// EvalError — ошибка eval (устаревший)
Кастомные классы ошибок
Для приложений важно создавать информативные ошибки с дополнительными полями:
class ValidationError extends Error {
constructor(message, field) {
super(message) // вызываем Error(message)
this.name = 'ValidationError'
this.field = field // дополнительное поле
}
}
class NotFoundError extends Error {
constructor(resource, id) {
super(`${resource} с id=${id} не найден`)
this.name = 'NotFoundError'
this.resource = resource
this.id = id
this.statusCode = 404 // удобно для HTTP API
}
}
// Использование
try {
const user = findUser(123)
} catch (error) {
if (error instanceof NotFoundError) {
// Знаем, что это за ошибка и у неё есть statusCode
res.status(error.statusCode).json({ message: error.message })
} else {
throw error // непредвиденная ошибка — пробрасываем
}
}
Важно установить
this.name = 'ValidationError' вручную — иначе error.name будет 'Error' из родительского класса. В некоторых проектах для корректного instanceof также нужно Object.setPrototypeOf(this, ValidationError.prototype) (при работе с TypeScript и target < ES6).Иерархия ошибок
Можно строить иерархии для группировки:
// Базовый класс для всех ошибок приложения
class AppError extends Error {
constructor(message, code) {
super(message)
this.name = this.constructor.name // автоматически берёт имя класса
this.code = code
}
}
class DatabaseError extends AppError {
constructor(message, query) {
super(message, 'DB_ERROR')
this.query = query
}
}
class NetworkError extends AppError {
constructor(message, url, status) {
super(message, 'NETWORK_ERROR')
this.url = url
this.status = status
}
}
// Проверка по иерархии
try {
await loadData()
} catch (error) {
if (error instanceof AppError) {
// Все ошибки приложения — обработаем в UI
showError(error.message)
} else {
// Системные ошибки — логируем и краш
logger.fatal(error)
throw error
}
}
Полезные свойства кастомных ошибок
class HttpError extends Error {
constructor(message, { status, url, method } = {}) {
super(message)
this.name = 'HttpError'
this.status = status // HTTP статус код
this.url = url // URL запроса
this.method = method // GET, POST, etc
this.isOperational = true // флаг: ожидаемая ошибка (не баг)
}
get isClientError() { return this.status >= 400 && this.status < 500 }
get isServerError() { return this.status >= 500 }
}