Menu
Русский

Типы ошибок в JavaScript: SyntaxError, TypeError и другие

Разбираемся во встроенных типах ошибок JavaScript: что означает каждый, когда он выскакивает и как читать сообщение, не гадая на кофейной гуще.

Ошибки — это объекты со своим типом

Когда в JavaScript выбрасывается ошибка, вам прилетает не просто строка с текстом, а полноценный объект. У этого объекта есть свой тип (конструктор) и несколько стандартных свойств: name, message и stack.

index.js
Output
Click Run to see the output here.

err.name — это короткая метка вроде "TypeError". В err.message лежит понятное человеку описание. А err instanceof TypeError подскажет, к какому конкретно классу относится ошибка. Понимать тип ошибки важно: так вы сразу видите, в чём дело — опечатка, негодное значение или код, который вообще не распарсился.

Всего в JavaScript семь встроенных типов ошибок. Три из них будут встречаться вам постоянно, остальные четыре — изредка.

SyntaxError: код не распарсился

SyntaxError означает, что JavaScript даже не смог прочитать ваш код. По сути это не ошибка времени выполнения — движок падает ещё на этапе парсинга, до того как выполнится хоть одна строка. Перехватить SyntaxError через try/catch в том же файле не получится, потому что весь файл отбраковывается целиком.

function greet(name {
    return "hi, " + name;
}
// SyntaxError: Unexpected token '{'

Пропущенная скобка, лишняя запятая, return вне функции — любое нарушение грамматики языка приводит к такой ошибке. Лечится это всегда одинаково: правим исходник. Единственный случай, когда SyntaxError реально можно перехватить, — это парсинг во время выполнения, например через JSON.parse:

index.js
Output
Click Run to see the output here.

JSON.parse получает строку уже во время выполнения, поэтому его синтаксические ошибки можно перехватить. А вот ошибки в самих исходниках — нет.

ReferenceError: такого имени не существует

ReferenceError в javascript возникает, когда вы обращаетесь к переменной, которая не объявлена ни в одной области видимости, доступной текущему коду.

index.js
Output
Click Run to see the output here.

В девяти случаях из десяти это банальная опечатка (totl вместо total). Оставшиеся 10% — проблемы с областью видимости: вы пытаетесь обратиться к переменной, объявленной в другой функции или модуле.

Есть ещё одна, менее очевидная причина — временная мёртвая зона (temporal dead zone). Объявления let и const существуют с самого начала блока, но обратиться к ним до строки с объявлением нельзя:

index.js
Output
Click Run to see the output here.

К моменту вызова console.log(x) переменная x уже существует как привязка, но ещё не проинициализирована. Отсюда и ошибка обращения. Чтобы починить — перенесите обращение ниже объявления.

TypeError в JavaScript: значение не того типа

TypeError означает, что значение-то есть, но оно не того сорта, который ожидает операция. Попытка вызвать то, что не является функцией, чтение свойства у null или undefined, присваивание в const — всё это TypeError.

index.js
Output
Click Run to see the output here.

«Cannot read properties of null (reading 'name')» — пожалуй, самая частая ошибка в JavaScript, которую ловят все без исключения. Лечится это либо гарантией того, что значение точно существует, либо опциональной цепочкой при обращении к свойству: user?.name.

Другие разновидности TypeError:

index.js
Output
Click Run to see the output here.

Попытка вызвать число как функцию, переприсвоить const, дёрнуть несуществующий метод — всё это значит, что значение оказалось не того типа, которого ждала операция.

RangeError: число вышло за допустимые границы

RangeError возникает, когда число само по себе валидное, но выходит за пределы, допустимые для конкретной операции.

index.js
Output
Click Run to see the output here.

Классический виновник — бесконечная рекурсия, от которой переполняется стек вызовов:

index.js
Output
Click Run to see the output here.

«Maximum call stack size exceeded» почти всегда означает, что функция вызывает саму себя без базового случая, либо две функции зациклились во взаимном вызове.

URIError и EvalError: редкие гости

URIError возникает при работе с функциями для обработки URI (encodeURI, decodeURIComponent и им подобными), когда на вход подаётся некорректная строка:

index.js
Output
Click Run to see the output here.

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

Цепочка наследования ошибок

Все эти типы наследуются от базового класса Error. А значит, для любого из них err instanceof Error вернёт true — это удобно, когда нужен универсальный catch:

index.js
Output
Click Run to see the output here.

Блок catch ловит всё подряд — в том числе и не-ошибки, если кто-то написал throw "oops". И вот эта последняя ветка как раз важна. Прежде чем обращаться с перехваченным значением как с объектом ошибки, всегда сужайте тип через instanceof.

Как выбросить ошибку в JavaScript вручную

Вы можете сами выбросить любой из встроенных типов ошибок, когда ваш код обнаружил проблему. Главное — подобрать тип под конкретный сбой:

index.js
Output
Click Run to see the output here.

Подбирать тип под конкретный сбой — это не просто косметика. Благодаря этому вызывающий код может писать точечные catch-блоки, а не разбирать текст сообщения об ошибке.

Свой класс ошибки в JavaScript

Когда встроенных типов не хватает, наследуемся от Error:

index.js
Output
Click Run to see the output here.

Запомните две вещи: обязательно вызывайте super(message), чтобы базовый Error корректно проинициализировался, и выставляйте this.name — тогда в логах будет правильная метка. А дополнительные поля вроде field позволяют вызывающему коду реагировать на конкретные сценарии сбоев, не разбирая строки сообщения.

Дальше: консоль и DevTools

Знать типы ошибок JavaScript — это только половина дела. Вторая половина — уметь читать стектрейс и заглядывать в состояние программы прямо во время её работы. DevTools в браузере (и отладчик в Node) превращают «упало, и я не понимаю почему» в пару секунд осмотра. Об этом и поговорим дальше.

Часто задаваемые вопросы

Какие встроенные типы ошибок есть в JavaScript?

В JavaScript их семь: базовый Error, а также SyntaxError, ReferenceError, TypeError, RangeError, URIError и EvalError. Каждый — это конструктор, который создаёт объект ошибки со свойствами name, message и stack. На практике чаще всего приходится иметь дело с SyntaxError, ReferenceError и TypeError.

В чём разница между SyntaxError и TypeError?

SyntaxError означает, что код вообще не является валидным JavaScript — движок не может его распарсить. TypeError — это когда код нормально распарсился, но во время выполнения произошло что-то недопустимое: например, вы пытаетесь вызвать не функцию или обратиться к свойству null. Синтаксические ошибки ломают весь скрипт целиком, а ошибки типов срабатывают только тогда, когда проблемная строка действительно выполняется.

Когда возникает ReferenceError в JavaScript?

Когда вы обращаетесь к имени, которое нигде не объявлено, либо к переменной let/const в её temporal dead zone — то есть до строки с объявлением. Самая частая причина — банальная опечатка: consoel.log(x) выбросит ReferenceError: consoel is not defined. Первым делом проверяйте написание и область видимости.

Можно ли создавать свои типы ошибок?

Да, достаточно унаследоваться от встроенного класса Error: class ValidationError extends Error { }. В конструкторе обязательно задайте this.name, чтобы в логах и в блоках catch можно было отличить одну ошибку от другой. Свои классы ошибок особенно удобны, когда разные ситуации требуют разной обработки.

Учитесь программировать с Coddy

НАЧАТЬ