Ленивая схема валидации в Yup реализуется через механизм
yup.lazy(), который позволяет отложить создание финальной
схемы до момента фактической валидации данных. Это означает, что
структура правил не фиксируется заранее, а вычисляется динамически на
основе входного значения.
Основной смысл подхода заключается в том, что схема становится функцией от данных:
Такой механизм особенно полезен при работе с неоднородными структурами данных, где форма объекта или массива зависит от внешних условий или значений внутри самого объекта.
yup.lazyФункция yup.lazy() принимает функцию, которая возвращает
схему:
Yup.lazy((value) => {
return schema;
});
Аргумент value — это текущее значение, которое проходит
валидацию. На основе этого значения возвращается соответствующая
схема.
Один из наиболее частых сценариев использования — выбор схемы в зависимости от типа данных:
import * as Yup from "yup";
const schema = Yup.lazy((value) => {
if (typeof value === "string") {
return Yup.string().min(3);
}
if (typeof value === "number") {
return Yup.number().min(10);
}
return Yup.mixed().notRequired();
});
Здесь поведение валидации зависит от того, строка это или число. Подобный подход позволяет объединять разнородные типы в одном поле без потери строгости проверки.
Частый случай применения ленивых схем — объекты, структура которых зависит от значения одного из полей.
const schema = Yup.object({
type: Yup.string().required(),
config: Yup.lazy((value, options) => {
const parent = options.parent;
if (parent.type === "A") {
return Yup.object({
aField: Yup.string().required(),
});
}
if (parent.type === "B") {
return Yup.object({
bField: Yup.number().required(),
});
}
return Yup.mixed().notRequired();
}),
});
В этом примере поле config меняет структуру в
зависимости от значения type. Это позволяет описывать
полиморфные объекты без необходимости создавать отдельные схемы для
каждого варианта.
yup.lazy() особенно полезен при валидации массивов,
содержащих элементы разных типов.
const schema = Yup.array().of(
Yup.lazy((value) => {
if (typeof value === "string") {
return Yup.string().min(2);
}
if (typeof value === "number") {
return Yup.number().positive();
}
return Yup.mixed();
})
);
Каждый элемент массива получает собственную схему, определяемую в момент проверки. Это позволяет обрабатывать смешанные коллекции без предварительной нормализации данных.
whenХотя Yup предоставляет метод when() для условной логики,
lazy() даёт более низкоуровневый контроль.
Сравнение подходов:
when() — декларативная условная логика;lazy() — программное формирование схемы.Пример, эквивалентный условной логике через lazy:
const schema = Yup.object({
role: Yup.string(),
permissions: Yup.lazy((_, options) => {
const role = options.parent.role;
switch (role) {
case "admin":
return Yup.array().of(Yup.string().required());
case "user":
return Yup.array().max(2);
default:
return Yup.array().max(0);
}
}),
});
Такой подход становится удобнее при сложной бизнес-логике, где количество условий велико.
Одно из ключевых применений yup.lazy() — рекурсивные
данные, например деревья.
const nodeSchema = Yup.lazy(() =>
Yup.object({
id: Yup.string().required(),
children: Yup.array().of(nodeSchema).notRequired(),
})
);
Здесь схема ссылается сама на себя, создавая возможность валидации вложенных структур произвольной глубины.
Функция внутри lazy() получает не только текущее
значение, но и контекст:
options.parent — родительский объект;options.path — путь к текущему значению;options.originalValue — исходное значение до
преобразований.Это позволяет строить схемы, зависящие от окружения:
const schema = Yup.object({
mode: Yup.string(),
value: Yup.lazy((val, options) => {
const mode = options.parent.mode;
if (mode === "strict") {
return Yup.number().required().min(100);
}
return Yup.mixed();
}),
});
Ленивая схема создаётся при каждой валидации конкретного значения. Это означает:
lazy() могут влиять на скорость
валидации.В высоконагруженных сценариях важно учитывать:
lazy().В TypeScript использование yup.lazy() требует явного
указания возвращаемого типа:
const schema = Yup.lazy((value: unknown): Yup.AnySchema => {
if (typeof value === "string") {
return Yup.string();
}
return Yup.number();
});
При более сложных структурах может потребоваться объединение типов
через Yup.MixedSchema или кастомные интерфейсы.
Несмотря на гибкость, подход имеет ряд особенностей:
В системах с жёсткой типизацией и фиксированными контрактами данных
использование lazy() оправдано только в строго ограниченных
местах.
yup.lazy() не заменяет стандартные схемы, а расширяет их
возможности. Он может быть встроен в любые конструкции:
Yup.object().shape({})Yup.array().of(...)Yup.mixed()Комбинация позволяет строить гибридные модели валидации, где статическая часть описывает базовую структуру, а динамическая — вариативные элементы.