Ленивая валидация в контексте Zod — это способ отложенного определения схемы до момента её фактического использования. В стандартном сценарии Zod требует, чтобы схема была полностью определена в момент объявления. Однако при работе с рекурсивными структурами, взаимными зависимостями или динамически формируемыми типами возникает необходимость откладывать вычисление схемы.
Основной механизм, реализующий ленивую валидацию, —
z.lazy.
z.lazyz.lazy принимает функцию, которая возвращает схему. Эта
функция вызывается только тогда, когда происходит валидация данных.
import { z } from "zod";
const schema = z.lazy(() => z.string());
В данном случае схема выглядит тривиальной, однако ключевой смысл заключается не в упрощении, а в отложенном вычислении.
Функция внутри z.lazy не выполняется сразу при
объявлении, что позволяет ссылаться на ещё не определённые
структуры.
Основное применение ленивой валидации связано с рекурсивными типами данных.
import { z } from "zod";
const Category = z.lazy(() =>
z.object({
name: z.string(),
children: z.array(Category).optional(),
})
);
Здесь Category ссылается на саму себя. Без
z.lazy такое определение невозможно, так как переменная ещё
не инициализирована в момент её использования.
Ленивая валидация используется не только для деревьев, но и для более сложных структур:
const Comment = z.lazy(() =>
z.object({
id: z.number(),
text: z.string(),
replies: z.array(Comment).default([]),
})
);
Каждый уровень вложенности обрабатывается одной и той же схемой, что обеспечивает консистентность структуры.
Ленивая валидация позволяет строить взаимосвязанные схемы, которые ссылаются друг на друга.
const User = z.lazy(() =>
z.object({
name: z.string(),
posts: z.array(Post),
})
);
const Post = z.lazy(() =>
z.object({
title: z.string(),
author: User,
})
);
Такой подход решает проблему циклических зависимостей, характерных для доменных моделей.
z.lazy влияет не на результат валидации, а на момент
построения схемы. При каждом обращении к схеме происходит вызов функции,
возвращающей актуальную версию структуры.
Важно учитывать:
z.lazy должна быть чистойЛенивая валидация добавляет минимальные накладные расходы, связанные с вызовом функции. Однако основной эффект проявляется не в замедлении, а в архитектурной гибкости.
Особенности:
z.lazy может комбинироваться с любыми типами Zod:
const Node = z.lazy(() =>
z.object({
value: z.number(),
next: Node.optional(),
})
);
const Tree = z.lazy(() =>
z.array(
z.object({
id: z.string(),
children: Tree.optional(),
})
)
);
const Expr = z.lazy(() =>
z.union([
z.object({ type: z.literal("num"), value: z.number() }),
z.object({
type: z.literal("add"),
left: Expr,
right: Expr,
}),
])
);
Несмотря на гибкость, ленивый подход имеет ряд ограничений:
const A = z.lazy(() => A);
Такое определение приводит к бесконечному циклу вызовов.
const A = z.lazy(() => z.string());
const B = z.lazy(() => z.number());
При взаимных ссылках такие схемы могут создавать логические конфликты, если используются в одной структуре.
Ленивая валидация используется в архитектурах, где данные имеют древовидную или взаимосвязанную структуру:
Ключевая задача z.lazy — устранение проблемы
циклического определения типов на уровне исполнения JavaScript.
В отличие от TypeScript, где циклы часто решаются на уровне типов, Zod решает их в рантайме, обеспечивая корректную валидацию данных независимо от порядка объявления.
При вызове .parse() или .safeParse():
z.lazyЛенивая валидация может использоваться совместно с
.transform():
const Node = z.lazy(() =>
z.object({
value: z.number(),
next: Node.optional(),
}).transform((data) => ({
...data,
hasNext: !!data.next,
}))
);
z.lazy позволяет формировать схемы на основе внешнего
состояния:
const createSchema = (strict) =>
z.lazy(() =>
z.object({
id: z.number(),
meta: strict ? z.object({}).strict() : z.object({}),
})
);
Такой подход используется при построении конфигурируемых валидаторов.
Ленивая валидация выполняет роль механизма разрешения зависимости времени объявления. Она позволяет строить модели данных, где структура определяется не линейно, а через сеть взаимных ссылок.
Это делает возможным описание сложных доменных моделей без дублирования схем и без ручного управления порядком их создания.