Runtime validation: Zod / Valibot / io-ts
TS типы стираются при компиляции, поэтому на границах системы нужна runtime-валидация. Zod — стандарт-де-факто. На интервью просят: «Как из API-ответа получить статически типизированные данные?» — через z.infer<typeof Schema>. Красный флаг — валидировать вручную через typeof и не знать про инференс типа из схемы.
Схема + вывод TS-типа + safeParse:
import { z } from 'zod';
const User = z.object({ id: z.string().uuid(), email: z.string().email(), age: z.number().int().nonnegative().optional(), role: z.enum(['admin', 'editor', 'viewer']),});
type User = z.infer<typeof User>;// { id: string; email: string; age?: number; role: 'admin'|'editor'|'viewer' }
async function loadUser(id: string): Promise<User> { const res = await fetch(`/api/users/${id}`).then(r => r.json()); return User.parse(res); // бросит ZodError при несоответствии}
const r = User.safeParse({ id: 'x', email: 'a', role: 'admin' });if (!r.success) console.log(r.error.issues);Композиция, преобразования и discriminated union:
import { z } from 'zod';
// Coerce + transformconst Date2 = z.string().transform(s => new Date(s));type DateT = z.infer<typeof Date2>; // Date
// Discriminated union в Zodconst Event = z.discriminatedUnion('type', [ z.object({ type: z.literal('click'), x: z.number(), y: z.number() }), z.object({ type: z.literal('key'), key: z.string() }),]);type Event = z.infer<typeof Event>;Итог: Zod-схема одновременно описывает runtime-валидацию и источник TS-типа через z.infer.