Типы ошибок: Error, TypeError, ReferenceError

JavaScript имеет иерархию встроенных типов ошибок, каждый из которых сигнализирует об определённой причине сбоя; понимание различий помогает быстро диагностировать проблему и корректно её обрабатывать.

Зачем нужно

Правильная идентификация типа ошибки в catch-блоке позволяет реагировать по-разному: показать пользователю сообщение при TypeError, перезагрузить данные при NetworkError, логировать критически при неизвестном Error. Знание причин каждого типа помогает быстро найти баг.

Где используется

  • Отладка и диагностика ошибок
  • Дифференцированная обработка в catch
  • Написание пользовательских ошибок, наследующих нужный тип
  • Тестирование (jest: expect(fn).toThrow(TypeError))

Иерархия встроенных ошибок

Error (базовый)
├── EvalError          — ошибки eval (редко)
├── RangeError         — значение вне допустимого диапазона
├── ReferenceError     — обращение к несуществующей переменной
├── SyntaxError        — синтаксическая ошибка в коде
├── TypeError          — неверный тип значения или операция
├── URIError           — неверный формат URI в encodeURI/decodeURI
└── AggregateError     — несколько ошибок (Promise.any)

Error — базовый тип

const err = new Error('Что-то пошло не так');
console.log(err.name);    // 'Error'
console.log(err.message); // 'Что-то пошло не так'
console.log(err.stack);   // стек вызовов

// throw работает с любым значением, но throw Error — стандарт
throw new Error('Сообщение');
throw 'строка'; // плохая практика — нет стека
throw 42;       // тоже плохая практика

TypeError — неверный тип

Самая частая ошибка в JavaScript:

// Обращение к свойству null/undefined
null.property;            // TypeError: Cannot read properties of null
undefined.method;       // TypeError: Cannot read properties of undefined
(null).toString();        // TypeError

// Вызов нефункции
const obj = { x: 1 };
obj.x;                  // TypeError: obj.x is not a function

// Неверный аргумент встроенного метода
Array.from(42);           // TypeError: 42 is not iterable

// Мутация frozen объекта
const frozen = Object.freeze({ x: 1 });
frozen.x = 2;             // TypeError (в strict mode): Cannot assign to read only property

ReferenceError — неизвестная переменная

// Обращение к необъявленной переменной
console.log(undeclared);  // ReferenceError: undeclared is not defined

// Переменная до объявления (temporal dead zone — let/const)
console.log(x);           // ReferenceError: Cannot access 'x' before initialization
let x = 5;

// var — нет ReferenceError, но есть hoisting
console.log(y);           // undefined (не ошибка!)
var y = 10;

RangeError — выход за допустимые границы

// Рекурсия без условия выхода
function infinite() { infinite; }
infinite; // RangeError: Maximum call stack size exceeded

// Невалидный размер массива
new Array(-1);   // RangeError: Invalid array length
new Array(2**32); // RangeError: Invalid array length

// Неверный аргумент
(1.234).toFixed(200); // RangeError: toFixed digits must be between 0 and 100

SyntaxError — синтаксис

// Обычно возникает при парсинге кода, до выполнения
// eval("let x = ;"); // SyntaxError: Unexpected token ';'
// JSON.parse("{bad json}"); // SyntaxError: Unexpected token
try {
  JSON.parse('{ не JSON }');
} catch (e) {
  console.log(e instanceof SyntaxError); // true
}

Обработка разных типов

async function safeOperation() {
  try {
    await riskyOperation;
  } catch (err) {
    if (err instanceof TypeError) {
      // Ошибка типа — скорее всего баг в коде
      console.error('Ошибка типа:', err.message);
      reportToBugTracker(err);

    } else if (err instanceof RangeError) {
      // Выход за диапазон — возможно некорректные данные
      console.error('Неверное значение:', err.message);
      showUserError('Некорректные данные');

    } else if (err instanceof SyntaxError) {
      // Синтаксическая ошибка — битые данные (напр. невалидный JSON)
      console.error('Неверный формат:', err.message);
      showUserError('Сервер вернул некорректный ответ');

    } else {
      // Неизвестная ошибка — пробрасываем
      throw err;
    }
  }
}

Свойства объекта Error

const err = new TypeError('Неверный тип');
err.name;    // 'TypeError'
err.message; // 'Неверный тип'
err.stack;   // строка со стеком вызовов (нестандартно, но поддерживается везде)
err.cause;   // ES2022: причина (другая ошибка)

// Error с cause
const original = new Error('Сеть недоступна');
const wrapped = new TypeError('Не удалось загрузить пользователя', { cause: original });
console.log(wrapped.cause); // Error: Сеть недоступна

Частые ошибки

1. Поглощение ошибок в catch

try {
  doSomething;
} catch (e) {
  // Плохо: все ошибки игнорируются
  console.log('ошибка'); // маскируем баг!
}

// Хорошо: обрабатываем только известные, остальные пробрасываем
} catch (e) {
  if (e instanceof KnownError) handleIt;
  else throw e; // не глотаем неизвестные ошибки
}

2. throw строки вместо Error

throw 'Ошибка!'; // нет стека, нет instanceof, нет name
throw new Error('Ошибка!'); // правильно

Связанные темы

Ресурсы