Error.isError(): Лучший способ проверки типов ошибки в JavaScript

Руководство по новому методу Error.isError() в JavaScript: почему он лучше instanceof, как решает проблемы кросс-контекстных ошибок и типизации в TypeScript.

Система обработки ошибок JavaScript долгое время оставалась слепым пятном при работе с ошибками в разных контекстах исполнения. Новый метод Error.isError() устраняет это ограничение, предоставляя разработчикам более надёжный способ идентификации объектов ошибок.

Традиционный подход к проверке является ли значение ошибкой — это использование оператора instanceof.

try {
// Код, который может выбросить ошибку
} catch (e) {
if (e instanceof Error) {
// Обработка ошибки
}
}

У этого подхода есть два существенных ограничения:

  1. Ошибки из разных областей неправильно идентифицируются. Когда ошибка исходит из другой области (например, из iframe или модуля VM), instanceof Error возвращает false, потому что у каждой области есть свой конструктор Error.

    const iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    const iframeError = iframe.contentWindow.Error;

    // ошибка в области iframe
    const crossRealmError = new iframeError('Error from iframe');

    // Эта проверка не работает, хотя это явная ошибка
    console.log(crossRealmError instanceof Error); // false 🔴

    // Error.isError() определяет её корректно
    console.log(Error.isError(crossRealmError)); // true 🟢

    Это может привести к тому, что ошибки обрабатываются некорректно, так как проверка instanceof не срабатывает.

  2. Фейковые ошибки могут пройти тест. Объекты, имеющие в цепочке прототипов Error.prototype, но не обладающие характеристиками ошибки, будут неверно идентифицированы как ошибки.

    // «Фейковая» ошибка, задав Error.prototype в цепочке прототипов
    const fakeError = { message: "I'm not a real error" };
    Object.setPrototypeOf(fakeError, Error.prototype);

    // Ошибочно идентифицируется как Error
    console.log(fakeError instanceof Error); // true 🔴

    // Error.isError() корректно отклоняет её
    console.log(Error.isError(fakeError)); // false 🟢

Эти проблемы могут привести к непоследовательной обработке ошибок и трудно диагностируемым багам. Новый метод Error.isError() предлагает решение:

try {
// Код, который может выбросить ошибку
} catch (e) {
if (Error.isError(e)) {
// Уверенная обработка ошибки
}
}

Вместо проверки цепочки прототипов Error.isError() использует более простой и надёжный подход. Используется специальный внутренний маркер (как скрытый ID тег), добавляемый к каждому настоящему объекту Error при его создании.

Этот метод работает лучше, чем instanceof, по двум причинам:

  1. Корректно идентифицирует ошибки, даже если они исходят из разных контекстов (например, iframe или модулей).
  2. Отбрасывает фейковые объекты, пытающиеся притвориться ошибками, манипулируя прототипом

Считайте, его проверкой водяного знака производителя вместо, просмотра этикетки — его сложнее подделать.

Типизация Error.isError()

Один из способов типизации метода Error.isError() в TypeScript:

try {
} catch (e: unknown) { // Код, который может выбросить ошибку
if (Error.isError(e)) { // 'e' сужается до типа Error
console.log(e.message); // Теперь она типобезопасная
} else { // Обработка исключений, не связанных с Error
console.log("Non-error was thrown:", e);
}
}

Комментарии


Дополнительные материалы

Предыдущая Статья

React: Какой useEffect запускается первым?

Следующая Статья

Что такое this в JavaScript