В больших codebase на TypeScript type guards и branded types часто создают излишнюю уверенность в типах, скрывая реальные рантайм-проблемы и влияя на производительность. Ошибки возникают при их использовании в production: фронтенд-приложения с API-клиентами, серверная валидация на Node.js, shared libraries — везде, где надо отделить один тип от другого.
Проблема с type guards
TypeScript “верит” твоему предикату, не проверяя его корректность внутри. После прохождения guard он забывает о других типах, что может привести к багам.
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function process(data: string | number | null) {
if (isString(data)) {
return data.toUpperCase(); // data: string, но если guard не учел null, рантайм баг
}
}Типичная ошибка: guard не обрабатывает null или undefined. В больших унионах это “съедает” целые ветки, и баг уходит в прод.
Branded types: иллюзия безопасности
Типа
type Email = string & { __brand: 'email' } — это попытка номинальной типизации. Но это работает только на уровне типов, без runtime-гарантий.type Email = string & { __brand: 'email' };
function isEmail(v: string): v is Email {
return v.includes('@');
}
// Любая строка с '@' считается Email, но brand отсутствует в runtimeВ большом проекте один неверный guard — и цепочка типов ломается, особенно при пересечениях с generics. Компилятор тупит на inference, что замедляет сборку и увеличивает время анализа.
Performance trade-offs
1. Кастомные guards не inlined — лишние вызовы в горячих путях (циклы, часто вызываемые функции) и growth bundle. 2. Branded types в generics приводят к замедлению type inference при вложенности. 3. Для реальной безопасности всё равно нужны runtime-проверки, которые дублируют логику и увеличивают latency.
Совет: используй простые встроенные проверки (
typeof, instanceof) для простых типов. Branded types оборачивай в конструкторы с runtime-валидацией:function createEmail(v: string): Email {
if (!v.includes('@')) throw new Error();
return v as Email;
}И обязательно покрывай guards unit-тестами на граничные значения: null, undefined, пустые строки — это 30% багов с типами.
Вывод: Type guards и branded types — инженерные компромиссы, где безопасность типов в compile-time стоит runtime-производительности и скрытых багов, если не добавлять параллельные runtime-проверки.