Перейти к содержимому
Теория · Ошибки · урок 5 из 10

Типы ошибок

Вернёмся к запискам из цепочки поручений. Одна записка сообщает «таблица пустая», другая — «таблицы нет вовсе». Тот, кто ловит записку, хочет сразу понять, что случилось. Поэтому у записки есть заголовок, а под ним — подробный текст.

Один блок catch ловит любую ошибку, а причины у ошибок разные. Функция userAge читает возраст из текста JSON и может сломаться двумя способами:

JavaScript
function userAge(text) {
    try {
        const data = JSON.parse(text);
        return data.user.age;
    } catch (e) {
        return e.name;
    }
}

console.log(userAge('{"user":{"age":30}}')); // 30
console.log(userAge("{ bad")); // SyntaxError
console.log(userAge("{}")); // TypeError

Давайте разберём три вызова:

  1. Текст '{"user":{"age":30}}' разобран без ошибок, и функция вернула 30.
  2. На неверном тексте JSON.parse бросил SyntaxError.
  3. В объекте {} нет свойства user, поэтому чтение data.user.age бросило TypeError.

Свойство name пойманной ошибки говорит, что именно случилось: сломан сам текст или в нём не хватает данных.

name и message

У каждого объекта ошибки есть два свойства:

  • name — вид ошибки, например "TypeError"; это заголовок записки;
  • message — её текст: у ошибки, которую вы создали сами, это строка, переданная в new Error(…).

Когда ошибку никто не поймал, Node печатает строку TypeError: …: это name, двоеточие и message. Ещё Node и браузеры добавляют свойство stack — строку со списком вызовов функций, которые привели к ошибке.

Этот вопрос часто задают на собеседовании: «Какие свойства есть у объекта Error?» Ответ: name и message, а Node и браузеры добавляют ещё stack.

Встроенные виды

Чаще всего JavaScript сам бросает ошибки четырёх встроенных видов. Разберём каждый.

TypeError

Действие не подходит к значению. Например:

  • чтение свойства у undefined;
  • вызов того, что не функция;
  • присваивание константе.

RangeError

Значение вне допустимых границ, например "ab".repeat(-1). В Node и Chrome так же называется ошибка слишком глубокой рекурсии.

ReferenceError

Имя, которого в программе нет, например опечатка в имени переменной.

SyntaxError

Неверный текст для JSON.parse. Синтаксическую ошибку в самом файле поймать нельзя: такой файл не запускается вовсе.

Наследование от Error

Каждый из этих видов — класс, который наследует Error: это то же наследование, что и у классов с extends из прошлой темы. Свой name каждый задаёт сам. Бросить такую ошибку можно и самому: throw new RangeError("age is out of range").

JavaScript
const e = new RangeError("age is out of range");
console.log(e.name); // RangeError
console.log(e.message); // age is out of range
console.log(e instanceof RangeError, e instanceof Error); // true true
console.log(e instanceof TypeError); // false

Разберём по строкам:

  1. new RangeError(…) создаёт ошибку вида RangeError с нашим текстом.
  2. e.name хранит вид ошибки: RangeError.
  3. e.message хранит текст, переданный в скобках.
  4. Обе проверки instanceof в четвёртой строке дают true: RangeError — это тоже Error.
  5. e instanceof TypeError даёт false: это другой вид.

Проверка instanceof Error верна для ошибки любого из этих видов, а instanceof RangeError — только для одного. Один catch ловит их все, а внутри него ошибки различают через instanceof или по e.name.

Здесь аналогия с заголовком перестаёт работать. Заголовок и текст записки пишет один человек. У ошибки вид задаёт её класс, а текст передаёт тот, кто её создаёт.

Частая ошибка

Общую проверку поставили раньше частной:

JavaScript
function describe(e) {
    if (e instanceof Error) {
        return "unknown error";
    } else if (e instanceof SyntaxError) { // ✗
        return "bad JSON";
    }
}

Сообщения об ошибке не будет, но вторая ветка не выполнится никогда. SyntaxError — тоже Error, поэтому describe(new SyntaxError("x")) возвращает "unknown error". Проверки отдельных видов ставят первыми, а instanceof Error — последней.

Главное

  • У объекта ошибки есть name — вид ошибки — и message — её текст; Node и браузеры добавляют stack.
  • TypeError, RangeError, ReferenceError и SyntaxError наследуют Error и задают свой name.
  • instanceof Error верна для любого вида, поэтому частные проверки ставят раньше общей.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом