Zod схемы

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

import { z } from "zod";

const userSchema = z.object({
  name: z.string(),
  age: z.number().int().positive(),
  email: z.string().email()
});

В этом примере создаётся схема объекта userSchema с обязательными полями name, age и email. Любые попытки валидировать объект с неправильными типами или отсутствующими полями приведут к ошибке.


Валидация данных

Для проверки данных используется метод .parse(). Он либо возвращает валидный объект с корректными типами, либо выбрасывает исключение ZodError.

const userData = {
  name: "Алексей",
  age: 25,
  email: "alex@example.com"
};

try {
  const validatedUser = userSchema.parse(userData);
  console.log(validatedUser);
} catch (err) {
  if (err instanceof z.ZodError) {
    console.log(err.errors);
  }
}

Метод .safeParse() предоставляет более безопасную альтернативу: вместо выбрасывания исключения возвращается объект с результатом и ошибками:

const result = userSchema.safeParse(userData);
if (result.success) {
  console.log(result.data);
} else {
  console.log(result.error.errors);
}

Составные и вложенные схемы

Zod позволяет комбинировать схемы для сложных структур:

const addressSchema = z.object({
  street: z.string(),
  city: z.string(),
  zip: z.string().regex(/^\d{5}$/)
});

const userWithAddressSchema = userSchema.extend({
  address: addressSchema
});
  • extend() добавляет новые поля к существующей схеме.
  • Вложенные схемы позволяют проверять сложные объекты, не повторяя код.

Для массивов используется z.array():

const usersArraySchema = z.array(userSchema);

Можно комбинировать массивы с ограничениями:

const limitedUsersSchema = z.array(userSchema).min(1).max(10);

Встроенные проверки и трансформации

Zod поддерживает цепочки проверок и трансформаций:

const trimmedString = z.string().min(1).max(50).transform(str => str.trim());

const positiveNumber = z.number().positive().int();
  • .min() и .max() задают ограничения длины или величины.
  • .regex() проверяет соответствие регулярному выражению.
  • .transform() позволяет модифицировать значение после валидации.

Для условной логики используется .refine():

const passwordSchema = z.string().min(8).refine(val => /[A-Z]/.test(val), {
  message: "Пароль должен содержать хотя бы одну заглавную букву"
});

Опциональные, nullable и default значения

Zod предоставляет удобные методы для работы с необязательными или nullable полями:

const optionalSchema = z.object({
  nickname: z.string().optional(),
  bio: z.string().nullable(),
  isActive: z.boolean().default(true)
});
  • .optional() — поле может отсутствовать.
  • .nullable() — поле может быть null.
  • .default(value) — если поле отсутствует, будет подставлено значение по умолчанию.

Интеграция с TypeScript

Zod автоматически генерирует типы TypeScript из схем:

type User = z.infer<typeof userSchema>;

const user: User = {
  name: "Мария",
  age: 30,
  email: "maria@example.com"
};
  • z.infer извлекает тип из схемы, что позволяет синхронизировать валидацию и типизацию без дублирования кода.

Расширенные возможности

  1. Unions и enums: Позволяют проверять переменные на несколько вариантов или фиксированный набор значений.
const roleSchema = z.enum(["admin", "user", "guest"]);
const mixedSchema = z.union([z.string(), z.number()]);
  1. Record и Map: Проверка объектов с динамическими ключами.
const dictionarySchema = z.record(z.string(), z.number());
  1. Lazy схемы: Для рекурсивных структур (например, дерево).
const categorySchema = z.object({
  name: z.string(),
  subcategories: z.lazy(() => z.array(categorySchema))
});

Пользовательские ошибки

Можно задавать свои сообщения об ошибках для каждой проверки:

const customErrorSchema = z.string().min(5, { message: "Минимум 5 символов" });

Ошибки возвращаются в виде массива с указанием пути до проблемного поля:

[
  { path: ["email"], message: "Некорректный email" },
  { path: ["age"], message: "Возраст должен быть положительным числом" }
]

Практические советы

  • Использовать safeParse для обработки пользовательского ввода без выброса исключений.
  • Создавать повторно используемые схемы для типичных сущностей (например, User, Address).
  • Комбинировать .transform() с .refine() для сложной логики проверки и преобразования данных.
  • Использовать .default() для упрощения обработки опциональных полей.

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