Zod как альтернатива

В экосистеме валидации данных в JavaScript традиционно доминирует подход, основанный на декларативных схемах и рантайм-проверках, где одна из самых известных библиотек — Yup. Однако в последние годы всё чаще рассматривается альтернативная модель, реализованная в Zod, которая делает ставку на строгую интеграцию с TypeScript и минимизацию дублирования типов.

Yup строится вокруг идеи описания схем через цепочки методов. Схема является отдельным объектом, который используется для проверки входных данных:

import * as Yup from 'yup';

const schema = Yup.object({
  email: Yup.string().email().required(),
  age: Yup.number().min(18).required()
});

await schema.validate(data);

В этом подходе схема и типы существуют параллельно. Даже при использовании TypeScript необходимо отдельно поддерживать типы интерфейсов, что создаёт дублирование логики.

Zod предлагает иной подход: схема одновременно является источником типов и валидатором:

import { z } from "zod";

const schema = z.object({
  email: z.string().email(),
  age: z.number().min(18)
});

type User = z.infer<typeof schema>;

Здесь тип User автоматически выводится из схемы, исключая необходимость синхронизации интерфейсов вручную.

Типизация и вывод типов

Одно из ключевых отличий заключается в уровне интеграции с TypeScript.

В Yup типизация существует отдельно:

interface User {
  email: string;
  age: number;
}

При изменении схемы необходимо вручную обновлять интерфейс, что повышает риск рассинхронизации.

В Zod типы выводятся автоматически через z.infer, что делает схему единственным источником истины. Это снижает вероятность ошибок, связанных с расхождением типов и валидаторов.

Модель выполнения и иммутабельность

Yup использует мутабельные цепочки методов. Это означает, что схема постепенно модифицируется:

Yup.string().required().min(5)

Каждый метод возвращает новый объект, но логика остаётся основанной на цепочках.

В Zod схемы изначально проектируются как иммутабельные структуры. Каждая трансформация создаёт новый экземпляр:

const base = z.string();
const refined = base.min(5);

Такой подход делает композицию более предсказуемой и облегчает повторное использование схем.

Обработка ошибок

Yup возвращает ошибки в формате массива, содержащего объекты с путями и сообщениями. Стандартный вывод требует дополнительной обработки:

try {
  await schema.validate(data);
} catch (err) {
  console.log(err.errors);
}

В Zod ошибки имеют структурированную форму ZodError, которая содержит дерево проблем:

try {
  schema.parse(data);
} catch (e) {
  console.log(e.flatten());
}

Структура ошибок позволяет точно локализовать проблему в сложных вложенных объектах без дополнительной обработки.

Валидация и парсинг данных

Yup разделяет методы validate и cast, где приведение типов часто происходит отдельно:

  • validate — проверка
  • cast — преобразование

Это создаёт два этапа обработки данных.

В Zod используется единый подход через parse и safeParse:

const result = schema.safeParse(data);

if (result.success) {
  console.log(result.data);
}

Данные либо полностью соответствуют схеме, либо возвращается структурированная ошибка без исключений.

Работа с трансформациями

Yup поддерживает трансформации через .transform():

Yup.string().transform(value => value.trim())

Однако цепочки трансформаций иногда становятся трудно отслеживаемыми при сложной логике.

В Zod трансформации интегрированы в поток валидации:

const schema = z.string().transform(val => val.trim());

Трансформация становится частью декларативного описания, что упрощает композицию.

Условная логика и композиция схем

Yup поддерживает условные схемы через .when():

Yup.string().when('isActive', {
  is: true,
  then: schema => schema.required()
});

Такие конструкции могут становиться громоздкими при росте сложности.

Zod решает задачу композиции через функции и объединение схем:

const base = z.string();

const conditional = (isActive: boolean) =>
  isActive ? base.min(1) : base.optional();

Это делает логику более явной и переносимой в обычные функции JavaScript/TypeScript.

Производительность и модель выполнения

Yup выполняет валидацию с использованием цепочек асинхронных и синхронных проверок, что может приводить к дополнительным накладным расходам в сложных схемах.

Zod оптимизирован под синхронную работу и минимизацию аллокаций. Основная идея заключается в том, что схема является чистой функцией преобразования входных данных в строго типизированный результат или ошибку.

Работа с вложенными структурами

Yup:

Yup.object({
  user: Yup.object({
    name: Yup.string().required()
  })
});

Zod:

const schema = z.object({
  user: z.object({
    name: z.string()
  })
});

При этом в Zod вложенные структуры автоматически участвуют в выводе типов, включая глубоко вложенные объекты и массивы, без дополнительной конфигурации.

Совместимость с API и формами

Yup исторически широко используется в связке с формами (например, React-экосистема), где его декларативность хорошо подходит для описания правил валидации на уровне UI.

Zod чаще применяется в слоях API, серверной логики и валидации входящих данных, где важна строгая типизация и контроль структуры данных на уровне доменной модели.

Эволюция подходов

Развитие схемной валидации в JavaScript демонстрирует переход от раздельных моделей (тип + валидатор) к унифицированной системе, где схема становится центральным элементом архитектуры. В этом контексте Zod представляет собой более современный подход, ориентированный на строгую типовую безопасность и композиционность, тогда как Yup остаётся зрелым инструментом с широкой историей применения и большим количеством интеграций в существующие проекты.