TGViewer
Настоящий JavaScript Настоящий JavaScript @true_js · 5.89K subscribers
Post #3602 319
⁣Conditional types в рантайме: как runtime type guards оживляют discriminated unions

Discriminated unions удобны в TypeScript, но в production их часто приходится проверять вручную через цепочки if и switch. Это ломает типы и множит ошибки на границе с внешними данными — API-ответами, JSON, AST-нодами. Решение: combine mapped types с runtime для автоматической генерации type guards.

Mapped type как контракт для runtime
Вместо того чтобы писать отдельный guard для каждого варианта union, определяем объект-гард через mapped type:

type ShapeGuards = {
[K in Shape['kind']]: (obj: unknown) => obj is Extract<Shape, { kind: K }>
};


Теперь для каждой ветки (circle, rect, group) нужно только реализовать валидацию полей. Тип обяжет вернуть правильный предикат.

Единая точка входа и generic фабрика
Добавляем общую функцию:

function isShape<T extends Shape['kind']>(kind: T, obj: unknown): obj is Extract<Shape, { kind: T }> {
return shapeGuards[kind](obj);
}


Типизация работает полностью: после isShape('circle', data) TypeScript знает, что data — это { kind: 'circle'; radius: number }. Для рекурсивных веток вроде group с вложенными Shape[] это тоже безопасно.

Можно уйти дальше — сделать generic-фабрику, которая возвращает строго типизированный guard для конкретного kind без кастингов внутри:

type GuardOfKind<T> = T extends Shape['kind']
? (obj: unknown) => obj is Extract<Shape, { kind: T }>
: never;

function createKindGuard<T extends Shape['kind']>(kind: T): GuardOfKind<T> {
return shapeGuards[kind] as any;
}


Типичная ошибка и trade-off
Ошибка: проверять только kind, а не поля варианта. TypeScript поведет, но в runtime придет некорректный объект — и сломается что-то ниже. Всегда проверяй специфичные поля: radius, shapes и т.д.

Минус подхода: для глубоко вложенных дифференцированных union придется писать много ручной валидации. Лечится либо кодогенерацией, либо внешними библиотеками (zod, io-ts). Но для легаси с 10+ ветками такой подход выигрывает у хаоса из switch и if-else.

Вывод: Соединение conditional типов с runtime guards превращает абстрактную типизацию в production-ready механизм проверки на границе доверия — особенно в API-клиентах, SDK и парсерах AST.
More from @true_js
  1. Oct 3, 2026Post #3963
  2. Oct 2, 2026😅 Айтишник отправил в одну компанию три одинаковых резюме и только одно дошло до финала О…
  3. Oct 2, 2026Post #3961
  4. Oct 2, 2026🤣 Правильно расставленные приоритеты в моей жизни би лайк: 💥 xCode Journal
  5. Oct 1, 2026🤯 Люди взбунтовались против «пыточной для ИИ» На GitHub заметили открытый проект AI Tortu…
  6. Oct 1, 2026День в Заонежье начинается ещё в дороге. Мы встретим вас в аэропорту или на вокзале. Дальш…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →