5 TypeScript-паттернов, которые AI не напишет за вас

Copilot и Claude генерируют TypeScript быстрее, чем вы успеваете подумать. Но есть паттерны, которые AI стабильно пропускает — потому что они требуют понимания архитектуры, а не синтаксиса

Вот 5 вещей, которые я раз за разом исправляю после AI-генерации:

1. Branded Types для бизнес-идентификаторов. AI напишет userId: string. Но в продакшене UserId и OrderId — это разные типы, и их нельзя путать. Branded types (type UserId = string & { __brand: 'UserId' }) ловят баги на этапе компиляции, а не на проде.

2. Exhaustive switch через never. AI обработает 3 из 4 кейсов в union type и не заметит. Паттерн с default: assertNever(x) гарантирует, что добавление нового варианта в union сломает компиляцию — а не молча сломает логику.

3. Result<T, E> вместо try/catch. AI оборачивает всё в try/catch и теряет типизацию ошибок. Паттерн Result (или Either) заставляет явно обрабатывать каждый тип ошибки. Особенно критично в domain layer, где бизнес-ошибки != технические ошибки.

4. Const assertions для конфигов. AI создаёт объект как Record<string, string>. Но as const + satisfies сохраняет literal types и autocompletion, одновременно проверяя структуру. Разница между "работает" и "работает и помогает".

5. Template Literal Types для API-контрактов. AI напишет path: string. Но type ApiPath = `/api/v${number}/${string}` ловит опечатки в URL на этапе компиляции. В проекте с 200+ эндпоинтами это экономит часы дебага.

Общий паттерн: AI оптимизирует на "чтобы работало". Архитектор оптимизирует на "чтобы не сломалось через полгода". TypeScript даёт инструменты для второго — но нужно знать, какие.

Какие TypeScript-паттерны вы считаете обязательными в production-коде?