Разбираемся с Error.isError() в JavaScript
Error.isError() проверяет настоящие ошибки JavaScript между realms, объясняет, почему он лучше instanceof Error, и показывает безопасный fallback.
Error.isError(value) — это статический метод, который возвращает true только тогда, когда value действительно является объектом Error, и остаётся надёжным даже при пересечении границ реалмов, поскольку проверяет внутреннюю метку ([[ErrorData]]), а не обходит цепочку прототипов.
Если вы когда-нибудь открывали свой трекер ошибок и обнаруживали пустой {} там, где должно было быть настоящее исключение, — вы уже сталкивались с проблемой, которую решает этот метод. Где-то по пути проверка instanceof тихо решила, что ваша ошибка — не ошибка. Метод был стандартизирован в ECMAScript 2026, а значит, давние пробелы в instanceof Error (ошибки из iframe, которые считываются как не-ошибки, и поддельные объекты, которые считываются как ошибки) теперь имеют штатное решение. В этой статье разбирается, что делает метод, почему он выигрывает у instanceof, каков точный механизм его работы, его краевые случаи и как внедрить его с безопасным запасным вариантом.
Поведение ровно такое, какого и следует ожидать: Error.isError(new Error()) — это true, а Error.isError({ message: 'x' }) — false, поскольку метод проверяет, как объект был сконструирован, а не просто то, от чего он наследуется.
Ключевые выводы
Error.isError()выполняет проверку по внутренней метке — слоту[[ErrorData]], — то есть ту же категорию неподделываемых проверок, что используетArray.isArray(), поэтому пользовательский код не сможет её обмануть.instanceof Errorдаёт сбой двумя противоположными способами:falseдля настоящей ошибки, созданной в другом реалме, иtrueдля поддельного объекта, прототипом которого назначилиError.prototype.- Метод возвращает
trueдля встроенных подклассов вродеTypeError, для классов, корректно наследующих черезextend Error, и дляDOMExceptionв браузерах — хотя Safari на данный момент возвращаетfalseдляDOMException. Error.isError()входит в ECMAScript 2026 и поставляется в Chrome/Edge 134+, Firefox 138+, Node.js 24.0.0+ и Safari 18.4 (частично).- Используйте его на границах (глобальные обработчики, связующий код логирования, воркеры, iframe, SSR/edge), где незаметный промах
instanceofпревращает настоящую ошибку в пустой объект в ваших логах.
Почему instanceof Error не справляется?
Discover how at OpenReplay.com.
instanceof Error даёт сбой двумя противоположными способами, и оба — незаметно. Предложение TC39 подробно описывает первый: настоящая ошибка, пересёкшая границу реалма — будь то из iframe или из модуля vm в Node, — возвращается как ложноотрицательный результат. У каждого реалма свой конструктор Error, поэтому ошибка, созданная в iframe, не является экземпляром вашего Error.
Второй сбой — обратный: любой объект, у которого в цепочке есть Error.prototype, проходит проверку, не будучи настоящей ошибкой. Вот каждый из сбоев в коде:
// Failure 1 — cross-realm error reads as NOT an error
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const crossRealmError = new iframe.contentWindow.Error('from iframe');
crossRealmError instanceof Error; // → false (wrong)
Error.isError(crossRealmError); // → true (correct)
// Failure 2 — fake object reads as an error
const fake = { message: "I'm not real" };
Object.setPrototypeOf(fake, Error.prototype);
fake instanceof Error; // → true (wrong)
Error.isError(fake); // → false (correct)
Оба результата — это задокументированный контракт, а не случайность. Справочник MDN по этому методу представляет его как надёжную альтернативу instanceof Error именно потому, что он избегает обоих сценариев отказа: заимствованного прототипа недостаточно, чтобы пройти проверку, а ошибка, созданная в другом реалме, всё равно её проходит. instanceof сравнивает идентичность конструктора вдоль цепочки прототипов, поэтому в обоих случаях ошибается.
| Входное значение | instanceof Error | duck-typing ('message' in x) | Error.isError() |
|---|---|---|---|
Межреалмовая Error (iframe/worker/vm) | ❌ false | ⚠️ зависит | ✅ true |
Object.setPrototypeOf(obj, Error.prototype) | ❌ true | ⚠️ true | ✅ false |
Экземпляр class MyError extends Error | ✅ true | ✅ true | ✅ true |
Как Error.isError() работает изнутри?
Внутри Error.isError() выполняет проверку по внутренней метке (branded check) для внутреннего слота, а не исследует цепочку прототипов. MDN описывает механизм напрямую: метод ищет приватное поле, которое конструктор Error() устанавливает на каждой создаваемой им ошибке. Это тот же приём, что лежит в основе Array.isArray(), и близкий родственник того, как оператор in проверяет наличие свойства.
Аналогия с Array.isArray() — та самая ментальная модель, которую стоит держать в голове. Array.isArray() также принимает массивы, созданные в другом реалме, где instanceof Array вернёт false, поскольку в каждом реалме свой конструктор Array. Error.isError() привносит ту же безопасную относительно реалмов маркировку в мир ошибок.
Текст спецификации на Stage 4 называет этот слот [[ErrorData]] и укладывает операцию IsError в три шага: всё, что не является объектом, немедленно не проходит; всё, что несёт этот слот, проходит; всё остальное — не проходит. Слот устанавливается при конструировании, и подделать его из JavaScript невозможно.
Почему слот, а не Object.prototype.toString? Потому что подмена тега сломала старый приём. Автор предложения вынес проблему на комитет: как только появился Symbol.toStringTag, проверка, которая была одновременно надёжной и неподделываемой, перестала быть и тем, и другим. А поскольку ничто за пределами Object#toString никогда не обращалось к слоту ошибки, у пользовательского кода вообще не осталось надёжного теста. Error.isError() закрывает ровно этот пробел.
Детали поведения, о которых стоит знать
Error.isError() возвращает true для всего семейства ошибок и false для всего остального, не выбрасывая исключений. Примеры MDN показывают, что new Error(), new TypeError() и new DOMException() возвращают true, тогда как вызов без аргумента или с передачей {}, null, undefined, 17 либо строки "Error" возвращает false. Поскольку предикат из спецификации возвращает false для любого не-объекта и для объектов без соответствующего слота, примитивы и null обрабатываются корректно, а не приводят к исключению.
Корректно расширенные пользовательские классы распознаются, поскольку наследуют метку:
class ValidationError extends Error {}
Error.isError(new ValidationError('bad input')); // → true
Отвергаются только подражатели, которые никогда не вызывают конструктор Error. Случай с DOMException содержит нюанс, который стоит запомнить. Правило MDN таково: экземпляры DOMException проходят проверку. Формально DOMException не является подклассом Error, поскольку его конструктор не наследуется от конструктора Error, но он несёт ту же метку, поэтому проверки по метке всё равно считают его ошибкой. Исключение — Safari: обзор месяца от Chrome, в котором вышел Firefox 138, фиксирует, что Safari отвечает false для DOMException, — именно поэтому метод не достиг статуса Baseline, несмотря на то что его уже реализовали все основные движки. MDN по той же причине по-прежнему помечает его как имеющий ограниченную доступность. Считайте этот единственный случай пока ещё не унифицированным.
Когда использовать Error.isError()
Используйте Error.isError() на границах (глобальные обработчики ошибок, связующий код логирования и отправки отчётов об ошибках, тест-раннеры, библиотеки, SSR/edge, воркеры, iframe и браузерные расширения), где незаметный промах instanceof превращает настоящую ошибку в пустой {} в ваших логах. Обычный instanceof вполне годится в узко ограниченном коде в пределах одного реалма; выигрыш проявляется именно на стыках, где значения пересекают контексты исполнения.
Это соответствует реальному сценарию отказа в отчётности: проверка instanceof на границе переклассифицирует настоящую выброшенную ошибку в обычный объект, и она попадает в ваш конвейер без сообщения и стека. Здесь полезна техника воспроизведения сессий (session replay): повтор сессии выявляет ошибку в консоли, которая была выброшена на самом деле, обнажая разрыв между тем, что увидел браузер, и тем, что сообщил ваш связующий код. Исправление — выполнять проверку по метке через Error.isError() на этих границах до того, как что-либо будет сериализовано или залогировано.
Поддержка в браузерах и средах выполнения, а также безопасный запасной вариант
Error.isError() входит в ECMAScript 2026, 17-е издание, которое Ecma International утвердила 30 июня 2026 года; предложение достигло Stage 4 на заседании TC39 в мае 2025 года. В браузерах он работает начиная с Chrome и Edge 134, Safari 18.4 и Firefox 138, выпущенного 29 апреля 2025 года. На сервере Node.js 24.0.0 получил его благодаря обновлению до V8 13.6, которое принесло его вместе с Float16Array, явным управлением ресурсами, RegExp.escape и WebAssembly Memory64.
Для замены «на месте», которая корректно деградирует на старых целевых платформах, используйте проверку наличия возможности:
function isError(value) {
return typeof Error.isError === 'function'
? Error.isError(value) // realm-safe on modern engines
: value instanceof Error; // fallback, not realm-safe
}
В TypeScript Error.isError(e) также работает как type guard, сужая перехваченное значение типа unknown до Error внутри ветки if, так что e.message типобезопасен без ручного приведения типа.
Заключение
Error.isError() закрывает пробел, с которым никогда не справлялись ни duck-typing, ни instanceof: он спрашивает, действительно ли движок пометил значение как ошибку, поэтому и межреалмовые ошибки, и подделки с подменённым прототипом определяются корректно. Переведите ваши граничные проверки (связующий код логирования, глобальные обработчики, стыки с воркерами и iframe) на обёртку с проверкой наличия возможности уже сегодня, а instanceof оставьте только там, где код никогда не покидает собственный реалм.
Часто задаваемые вопросы
Error.isError() стандартизирован или это всё ещё экспериментальное предложение?
Error.isError() полностью стандартизирован. Он перешёл на Stage 4 процесса TC39 на 108-м заседании в мае 2025 года и включён в ECMAScript 2026, 17-е издание спецификации языка. Это больше не предложение и не экспериментальная возможность, поэтому описания, называющие его «ещё не стандартизированным» или «Stage 3», устарели. Считайте его выпущенной возможностью языка.
Работает ли Error.isError() с пользовательскими классами ошибок?
Да, при условии, что класс корректно расширяет Error. Класс, объявленный как 'class MyError extends Error {}', наследует внутреннюю метку, устанавливаемую конструктором Error, поэтому Error.isError(new MyError()) возвращает true. Отвергаются лишь объекты-подражатели, которые никогда не вызывают конструктор Error, — например, обычный объект, которому принудительно вставили Error.prototype в цепочку прототипов. Требование — корректное наследование, а не имя класса.
Работает ли Error.isError() в Safari?
Safari 18.4 и более поздние версии поддерживают Error.isError() для обычных объектов Error, но поддержка частичная. На данный момент Safari возвращает false для экземпляров DOMException, тогда как спецификация и другие движки возвращают true. Из-за этого расхождения MDN не относит метод к Baseline, а web.dev отмечает, что он пока доступен неравномерно. Обрабатывайте случай с DOMException защитно, если ваш код рассчитан на Safari.
Быстрее ли Error.isError(), чем instanceof Error?
Обе проверки фактически выполняются за константное время, поэтому производительность — не повод для перехода. instanceof обходит цепочку прототипов, а Error.isError() считывает одну внутреннюю метку, но практическая разница пренебрежимо мала. Настоящее преимущество — корректность: Error.isError() даёт верный ответ для межреалмовых ошибок и подделок с подменённым прототипом, то есть в случаях, где instanceof незаметно ошибается. Выбирайте его ради надёжности на границах контекстов исполнения, а не ради скорости.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k