🪲 Ошибки надо возвращать (return), а не выкидывать (throw)
Короче, я раньше к этой теме подходил деликатно, типа "это полезно", "было бы круто, если бы вы делали так", "ну если вы решитесь" и так далее, а сейчас полностью убедился, что это точно верный подход и другого советовать не хочу.
У этого подхода куча преимуществ: (1) вы четко видете какие виды ошибок возвращаете (особенно если создаете кастомные ошибки), (2) нет неявных перехватчиков ошибок, (3) try очень громоздкая и некрасивая конструкция, которая может добавить несколько уровней вложенности и много других полезных пунктов.
Ну и всегда был концептуальный вопрос, которому посвящены множество конференций и статей: почему я должен выбрасывать (throw) ошибку, которая является нормальной частью бизнес-логики (например, NotFound на сущность, которой реально может не быть) при том, что это может убить мою программу, если не будет отловлено.
Что так резко меня убедило?
1. Исторически так было в C.
2. Далее эту фишку подхватил Go.
3. В Rust такая же история, НО они возвращают типа монаду Result<T, Error>.
4. И тут я узнаю, что в Zig (языке, на который я делаю сейчас огрмную ставку) прям встроенный оператор Union Type для возврата ошибок.
Ну, а про функциональные языки даже говорить не буду, там это просто стандарт, тоже через монады.
Если использовать TS, то самый адекватный вариант это: type Result<T, E extends Error = Error> = T | E – могу потом рассказать почему.
Да, есть некоторые места (например, валидация отдельных свойств), где это может сильно засрать код, но и для этого есть решение (об этом в следующих постах)
Мне теперь интересно: насколько вам заходит идея возвращать ошибки, а не выбрасывать их?