Перейти к содержимому

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&lt;typeof User&gt;;
// { id: string; email: string; age?: number; role: 'admin'|'editor'|'viewer' }
async function loadUser(id: string): Promise&lt;User&gt; {
const res = await fetch(`/api/users/${id}`).then(r =&gt; 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 + transform
const Date2 = z.string().transform(s =&gt; new Date(s));
type DateT = z.infer&lt;typeof Date2&gt;; // Date
// Discriminated union в Zod
const 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&lt;typeof Event&gt;;

Итог: Zod-схема одновременно описывает runtime-валидацию и источник TS-типа через z.infer.