← На главную

TypeScript: меняем валидацию на парсинг с брендированными типами и Zod

30.06.2026 10:48 · hackernews

В TypeScript проверки данных вроде if (user.email.includes("@")) в большинстве кодовых баз — это валидация, а не парсинг. Разница принципиальная. Валидатор просто говорит «ок», но тут же выбрасывает результат проверки. Парсер принимает сырой blob, а отдаёт либо точный тип (например, Email вместо string), либо ошибку. Информация никуда не девается — она закодирована в типе.

В Haskell или Elm так пишут по умолчанию. TypeScript не подталкивает к этому, а структурная типизация активно мешает. Тип string остаётся просто строкой, даже если вы трижды проверили, что это email. Обходной путь, который придумало сообщество — брендированные типы (branded types). Внутри модуля объявляется unique symbol, и тип строится как пересечение: type Email = string & { readonly [Brand]: true }. На runtime поля нет — это «фантом». Снаружи модуля никто не сможет сфабриковать такой тип. Email нельзя передать туда, где ждут просто string, а наружу — можно.

Функция-парсер становится единственным доверенным местом, где разрешён каст (as Email). Всё остальное в коде обязано вызывать парсер и обрабатывать оба варианта Parsed<T>: ok и err. Использовать kind: "ok" | "err" с discriminated union, а не boolean — иначе при добавлении третьего варианта TypeScript не подскажет. Для проверки на полноту ветвлений пишется never-ловушка в default. JSON.parse возвращает any. Его сразу надо аннотировать как unknown — пусть парсер разбирается.

Автор не предлагает писать всё руками. Zod, io-ts или valibot делают то же самое в декларативном стиле и дают тип и парсер из одной схемы. Но библиотека не решает проблему мышления. Дисциплина остаётся за разработчиком: использовать парсинг на каждой границе, не срезать углы через as. Принцип Алексис Кинг сводится к простому правилу: «заставляйте систему типов нести доказательство, а не вашу память». Каждая проверка, результат которой не закодирован в типе, ложится на плечи будущего вас. И будущий вы об этом забудет.

Читать оригинал →