Zod для TypeScript: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в Zod: что это и зачем нужно

В современной веб-разработке на TypeScript одной из ключевых проблем является обеспечение целостности данных. Даже при строгой типизации, TypeScript не может гарантировать, что данные, пришедшие из внешних источников (API, формы, localStorage, WebSocket), действительно соответствуют заявленным типам. Компилятор TypeScript работает на этапе разработки, но во время выполнения программы типы стираются. Именно здесь на помощь приходит Zod.

Zod — это библиотека для декларативной валидации и типизации данных для TypeScript и JavaScript. Она позволяет создавать схемы (schemas), которые одновременно служат и валидатором данных во время выполнения, и источником истины для статических типов TypeScript. Основная философия Zod — "одна схема, два назначения": вы пишете схему один раз, а получаете и валидацию, и автоматически выведенный тип.

Ключевые преимущества Zod:

  • Безопасность типов: автоматический вывод TypeScript-типов из схем.
  • Декларативность: читаемый и лаконичный синтаксис.
  • Композиционность: схемы можно комбинировать и расширять.
  • Детальные ошибки: понятные сообщения об ошибках валидации.
  • Нулевая зависимость: библиотека не требует дополнительных пакетов.

Установка Zod

Zod — это npm-пакет, поэтому установка производится через менеджер пакетов Node.js. Библиотека написана на TypeScript и поставляется с готовыми файлами деклараций типов, поэтому дополнительная установка @types/zod не требуется.

# Установка через npm
npm install zod

# Установка через yarn
yarn add zod

# Установка через pnpm
pnpm add zod

После установки вы можете импортировать Zod в свой проект:

import { z } from "zod";

Обратите внимание: Zod не устанавливается через pip, так как это библиотека для экосистемы JavaScript/TypeScript, а не Python.

Быстрый старт с примером кода

Давайте создадим простую схему для валидации данных пользователя и сразу увидим, как Zod объединяет валидацию и типизацию.

import { z } from "zod";

// 1. Определяем схему
const UserSchema = z.object({
    id: z.number().positive(),
    name: z.string().min(2, "Имя должно содержать минимум 2 символа"),
    email: z.string().email("Некорректный email"),
    age: z.number().int().min(18, "Возраст должен быть не менее 18 лет").optional(),
});

// 2. Автоматически выводим тип из схемы
type User = z.infer<typeof UserSchema>;

// 3. Валидируем данные
const validData = {
    id: 1,
    name: "Анна",
    email: "anna@example.com",
    age: 25,
};

const result = UserSchema.safeParse(validData);

if (result.success) {
    // result.data имеет тип User
    console.log("Валидация успешна:", result.data);
} else {
    // result.error содержит детальную информацию об ошибках
    console.log("Ошибки валидации:", result.error.errors);
}

// Пример с некорректными данными
const invalidData = {
    id: -1,
    name: "А",
    email: "не-email",
};

const invalidResult = UserSchema.safeParse(invalidData);
if (!invalidResult.success) {
    console.log("Ошибки:", invalidResult.error.errors);
    // Выведет:
    // - id: должно быть положительным числом
    // - name: Имя должно содержать минимум 2 символа
    // - email: Некорректный email
}

Основные методы и функции Zod

В этом разделе мы подробно рассмотрим 10 ключевых методов Zod, которые покроют 90% ваших задач по валидации.

1. z.string() — Валидация строк

Что делает: Создает схему для валидации строковых значений. Предоставляет цепочку методов для наложения дополнительных ограничений.

Параметры: Не принимает параметров при создании. Цепочка методов: .min(), .max(), .length(), .email(), .url(), .regex(), .includes(), .startsWith(), .endsWith(), .trim(), .toLowerCase(), .toUpperCase().

const EmailSchema = z.string().email("Некорректный email").min(5).max(255);
const UrlSchema = z.string().url("Должен быть валидный URL").startsWith("https://");

console.log(EmailSchema.safeParse("test@test.com").success); // true
console.log(EmailSchema.safeParse("invalid").success); // false
console.log(UrlSchema.safeParse("https://example.com").success); // true

2. z.number() — Валидация чисел

Что делает: Создает схему для числовых значений. Поддерживает цепные методы для ограничения диапазона и типа числа.

Параметры: Методы: .min(), .max(), .int() (целое число), .positive(), .negative(), .nonnegative(), .nonpositive(), .multipleOf(), .finite().

const AgeSchema = z.number().int().positive().min(0).max(150);
const PriceSchema = z.number().positive().multipleOf(0.01); // для денежных значений

console.log(AgeSchema.safeParse(25).success); // true
console.log(AgeSchema.safeParse(-5).success); // false (отрицательное)
console.log(PriceSchema.safeParse(10.99).success); // true
console.log(PriceSchema.safeParse(10.999).success); // false (не кратно 0.01)

3. z.boolean() — Валидация булевых значений

Что делает: Проверяет, является ли значение логическим (true или false).

Параметры: Не имеет дополнительных методов валидации.

const IsActiveSchema = z.boolean();

console.log(IsActiveSchema.safeParse(true).success); // true
console.log(IsActiveSchema.safeParse("true").success); // false (строка, не boolean)
console.log(IsActiveSchema.safeParse(1).success); // false (число, не boolean)

4. z.array() — Валидация массивов

Что делает: Создает схему для массива, элементы которого проходят валидацию по переданной схеме элемента.

Параметры: Принимает схему элемента. Методы: .min(), .max(), .length(), .nonempty().

const StringArraySchema = z.array(z.string().min(1));
const NumberArraySchema = z.array(z.number().int()).min(1).max(10);

console.log(StringArraySchema.safeParse(["a", "b", "c"]).success); // true
console.log(StringArraySchema.safeParse(["", "b"]).success); // false (пустая строка)
console.log(NumberArraySchema.safeParse([1, 2, 3]).success); // true
console.log(NumberArraySchema.safeParse([]).success); // false (пустой массив)

5. z.object() — Валидация объектов

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

Параметры: Принимает объект, где ключи — имена полей, а значения — схемы для этих полей. Методы: .partial() (все поля опциональны), .required(), .pick() (выбрать поля), .omit() (исключить поля), .extend() (расширить схему), .merge() (объединить схемы).

const UserSchema = z.object({
    id: z.number(),
    name: z.string(),
    email: z.string().email(),
});

const PartialUserSchema = UserSchema.partial(); // все поля опциональны
const UserWithRoleSchema = UserSchema.extend({
    role: z.enum(["admin", "user"]),
});

console.log(UserSchema.safeParse({ id: 1, name: "Test", email: "t@t.com" }).success); // true
console.log(PartialUserSchema.safeParse({}).success); // true
console.log(UserWithRoleSchema.safeParse({ id: 1, name: "T", email: "t@t.com", role: "admin" }).success); // true

6. z.enum() — Валидация перечислений

Что делает: Создает схему, которая принимает только одно из перечисленных значений. Аналог TypeScript enum.

Параметры: Принимает кортеж строковых значений или массив через z.enum([...]).

const ColorSchema = z.enum(["red", "green", "blue"]);
const StatusSchema = z.enum(["active", "inactive", "pending"]);

console.log(ColorSchema.safeParse("red").success); // true
console.log(ColorSchema.safeParse("yellow").success); // false
console.log(StatusSchema.safeParse("active").success); // true

7. z.union() — Объединение схем (anyOf)

Что делает: Создает схему, которая проходит валидацию, если данные соответствуют хотя бы одной из переданных схем. Аналог TypeScript union type.

Параметры: Принимает массив схем.

const StringOrNumberSchema = z.union([z.string(), z.number()]);
const IdSchema = z.union([z.number().positive(), z.string().uuid()]);

console.log(StringOrNumberSchema.safeParse("hello").success); // true
console.log(StringOrNumberSchema.safeParse(42).success); // true
console.log(StringOrNumberSchema.safeParse(true).success); // false
console.log(IdSchema.safeParse("550e8400-e29b-41d4-a716-446655440000").success); // true

8. z.optional() — Опциональные значения

Что делает: Делает значение опциональным — валидация проходит, если значение undefined или соответствует внутренней схеме.

Параметры: Принимает схему, которую нужно сделать опциональной.

const NameSchema = z.string().min(2);
const OptionalNameSchema = z.optional(NameSchema);

console.log(OptionalNameSchema.safeParse("Анна").success); // true
console.log(OptionalNameSchema.safeParse(undefined).success); // true
console.log(OptionalNameSchema.safeParse("А").success); // false (меньше 2 символов)

9. z.nullable() — Допустимость null

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

Параметры: Принимает схему.

const NullableStringSchema = z.nullable(z.string());

console.log(NullableStringSchema.safeParse("test").success); // true
console.log(NullableStringSchema.safeParse(null).success); // true
console.log(NullableStringSchema.safeParse(undefined).success); // false

10. z.custom() — Пользовательская валидация

Что делает: Позволяет создавать собственные валидаторы для случаев, когда встроенных методов недостаточно.

Параметры: Принимает функцию-валидатор, которая возвращает boolean или выбрасывает ошибку.

const PasswordSchema = z.string()
    .min(8, "Пароль должен содержать минимум 8 символов")
    .refine((val) => /[A-Z]/.test(val), "Пароль должен содержать заглавную букву")
    .refine((val) => /[0-9]/.test(val), "Пароль должен содержать цифру");

const EvenNumberSchema = z.number().refine((n) => n % 2 === 0, "Число должно быть четным");

console.log(PasswordSchema.safeParse("Strong1").success); // true
console                    

Рекомендаций