Профессиональная обработка ошибок в TypeScript

Обработке ошибок в Type/JavaScript обычно не уделяется должного внимания. Между тем, для долговечности любого проекта очень важно выявлять и регистрировать ошибки.

Начав чаще программировать на TypeScript, я понял, что слишком мало занимаюсь обработкой ошибок. Часто сталкивался с такой проблемой:

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

В JavaScript практически все можно обработать оператором throw:

Таким образом, перехватываемая ошибка (error) действительно является неопределенной (unknown).

Тем не менее есть способы чистой обработки ошибок в TypeScript. За долгие годы работы я создал шаблон, который предпочитаю использовать для этих целей.


Основные типы ошибок в JavaScript

Среди множества ошибок в JavaScript наиболее распространенными являются следующие.

  • ReferenceError  —  код ссылается на несуществующую переменную.
  • TypeError  —  значение не соответствует ожидаемому типу.
  • SyntaxError  —  синтаксически неверный код.

Генерация ошибок с помощью оператора throw

Бывают случаи, когда ошибку нужно сгенерировать вручную. Например, когда код зависит от значения, возвращаемого при вызове функции, но есть вероятность того, что значение будет undefined, или TypeScript будет считать его таковым. В данном примере генерация ошибки  —  лучшее решение для сужения возвращаемого user.

Перехват ошибок с помощью оператора catch

Сгенерированная ошибка всплывает в стеке вызовов до тех пор, пока не будет перехвачена в конструкции try/catch. Как только код, выполняемый внутри блока try, генерирует ошибку, она будет “поймана” в блоке catch. Ошибка может исходить от функции, вложенной внутрь функции, и будет всплывать до тех пор, пока не будет перехвачена.

Сужение типа ошибки

После перехвата сгенерированной ошибки полезно проверить ее тип. Это позволяет сузить тип от unknown до определенного типа, с которым можно взаимодействовать. Выполним это с помощью instanceof.


Шаблон проектирования

В своем последнем проекте я сгруппировал код по доменам в каталогах с названием Features. Суть заключается в том, что каталог Feature содержит код для конкретного домена. Он может включать в себя связанные компоненты, хуки, типы, ошибки и многое другое.

В этой статье нас интересуют ошибки. Каждый каталог Feature содержит файл errors.ts, в котором я определяю пользовательский класс ошибок для соответствующего домена.

Создание пользовательского типа ошибки

В файле errors.ts я экспортирую class. Я поддерживаю тип union для потенциальных имен, что позволяет использовать автодополнение ввода и увеличивает степень безопасности типов. Класс расширяет объект Error, что позволяет добавлять отслеживание стека (для большинства JS-сред выполнения).

Генерирование пользовательской ошибки

При инстанцировании новой ошибки значение имени, поддерживаемое системой автодополнения ввода, должно быть одним из имен, определенных в типе union.

Перехват пользовательской ошибки

Тип перехваченной ошибки можно сузить с помощью instanceof. После сужения error.name дает возможность автодополнения ввода. В этот момент можно выполнить логические действия, основанные на имени сгенерированной ошибки.

В данном примере PROJECT_LIMIT_REACHED  —  это ошибка, и ее нужно показать пользователю, предоставив сообщение, которое должно быть отображено специально для него.

Создание многократно используемой базы ошибок

При наличии большого количества файлов errors.ts стоит убедиться, что код соответствует основному принципу разработки Don’t Repeat Yourself (“не повторяйся”). Единственный динамический код в классе  —  это тип имен union. Поэтому я создаю класс ErrorBase, который принимает generic, используемый в качестве типа имени.

Теперь, создав новый пользовательский класс ошибок, я могу расширить эту базу, задав ей тип доступных имен union.

Заключение

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

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Kolby Sisk: Handling errors like a pro in TypeScript

Предыдущая статьяСупербыстрый веб-фреймворк Astro: подробный обзор
Следующая статьяПошаговое руководство по NLP: конструирование признаков текстовых данных