Объекты и их валидация

Объектная схема в Zod строится вокруг функции z.object(), которая позволяет описывать форму JavaScript-объекта через набор строго типизированных полей. Каждый ключ объекта ассоциируется с отдельной схемой, что обеспечивает детальную проверку структуры входных данных на уровне рантайма.

import { z } from "zod";

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

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


Базовое определение объекта

z.object() принимает объект, где ключи соответствуют полям, а значения — схемам.

const ProductSchema = z.object({
  title: z.string(),
  price: z.number(),
  inStock: z.boolean(),
});

Важный аспект: структура объекта фиксируется строго по ключам. Лишние поля по умолчанию удаляются (поведение зависит от режима: strip, strict, passthrough).


Строгий, пасстру и «очищающий» режимы

strip-режим (по умолчанию)

Лишние поля удаляются:

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

schema.parse({
  name: "Book",
  extra: 123,
});

Результат: { name: "Book" }


strict-режим

Запрещает любые лишние поля:

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

Вход с дополнительными ключами приведёт к ошибке валидации.


passthrough-режим

Сохраняет все дополнительные поля:

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

Результат сохраняет как определённые, так и неизвестные поля.


Частичные объекты (partial)

Метод partial() делает все поля необязательными:

const schema = z.object({
  name: z.string(),
  age: z.number(),
}).partial();

Теперь допустим объект:

{ name: "Alex" }

Каждое поле оборачивается в optional().


Выбор и исключение полей (pick / omit)

pick — выбор конкретных ключей

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

const NameOnly = UserBase.pick({
  name: true,
});

omit — исключение полей

const PublicUser = UserBase.omit({
  email: true,
});

Расширение схем (extend)

Метод extend() добавляет новые поля к существующей схеме:

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

const ExtendedUser = BaseUser.extend({
  email: z.string().email(),
});

Это основной механизм композиции объектных схем.


Объединение схем (merge)

merge() объединяет две объектные схемы:

const A = z.object({
  a: z.string(),
});

const B = z.object({
  b: z.number(),
});

const C = A.merge(B);

Результирующая схема содержит поля обеих структур.


Глубокая частичность (deep partial)

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

const schema = z.object({
  user: z.object({
    name: z.string(),
    profile: z.object({
      age: z.number(),
    }),
  }),
}).deepPartial();

Все уровни вложенности становятся необязательными.


Вложенные объекты

Zod поддерживает неограниченную вложенность:

const schema = z.object({
  user: z.object({
    id: z.number(),
    settings: z.object({
      theme: z.string(),
    }),
  }),
});

Каждый уровень проходит независимую валидацию.


Неизвестные ключи: catchall

catchall() определяет поведение для неизвестных свойств:

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

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


Типизация записей (Record внутри объектов)

Для динамических ключей используется z.record():

const schema = z.object({
  translations: z.record(z.string()),
});

Это полезно для словарей, где ключи неизвестны заранее.


Преобразование и валидация объектов

parse

Жёсткая проверка, выбрасывает исключение при ошибке:

schema.parse(data);

safeParse

Возвращает результат без исключений:

const result = schema.safeParse(data);

if (!result.success) {
  console.log(result.error);
}

Рефайн и уточнение объектов

Метод refine() добавляет пользовательскую проверку:

const schema = z.object({
  password: z.string(),
  confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
  message: "Пароли не совпадают",
});

Используется для межполейной логики.


Ленивая рекурсия (lazy objects)

Для самоссылочных структур применяется z.lazy():

const Category = z.lazy(() =>
  z.object({
    name: z.string(),
    children: z.array(Category),
  })
);

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


Строгая форма объектов и контроль схемы

z.strictObject() используется для жёсткого контроля структуры:

const schema = z.strictObject({
  id: z.number(),
});

Любые дополнительные поля считаются ошибкой, независимо от глобального режима.


Ключевые особенности объектной модели

  • строгая типизация ключей и значений
  • контроль лишних полей на уровне схемы
  • композиция через extend, merge, pick, omit
  • поддержка вложенности любой глубины
  • возможность динамических ключей через record
  • рекурсивные структуры через lazy
  • расширяемая валидация через refine

Ошибки валидации объектов

Ошибки формируются на уровне каждого поля и агрегируются в структуру ZodError. Внутри содержится путь (path) до конкретного поля, что позволяет точно локализовать проблему:

{
  path: ["user", "email"],
  message: "Invalid email"
}

Такая структура критична при работе с глубоко вложенными объектами и формами данных.