Библиотека Yup предоставляет декларативный механизм описания схем
валидации, где каждое поле может зависеть от состояния других полей
объекта. Это позволяет строить не статические правила, а динамические,
изменяющиеся в зависимости от контекста данных. Ключевым инструментом
для реализации таких сценариев выступает метод when, а
также расширенный механизм test, который позволяет задавать
произвольную логику проверки.
Условная валидация становится критически важной в формах, где структура данных не фиксирована: регистрационные формы с разными типами пользователей, конфигурационные интерфейсы, многошаговые формы и сложные бизнес-правила.
Метод when используется для привязки валидации поля к
значению другого поля. Базовая форма предполагает один зависимый
параметр:
import * as Yup from 'yup';
const schema = Yup.object({
isCompany: Yup.boolean(),
companyName: Yup.string().when('isCompany', {
is: true,
then: schema => schema.required('Укажите название компании'),
otherwise: schema => schema.notRequired()
})
});
Здесь логика проста: если isCompany истинно, поле
становится обязательным.
Yup позволяет учитывать сразу несколько полей при помощи массива зависимостей:
const schema = Yup.object({
role: Yup.string(),
isActive: Yup.boolean(),
accessCode: Yup.string().when(['role', 'isActive'], {
is: (role, isActive) => role === 'admin' && isActive,
then: schema => schema.required(),
otherwise: schema => schema.notRequired()
})
});
В этом случае проверка выполняется на основе комбинации значений. Это первый уровень многомерных условий, где логика становится функцией от нескольких переменных.
Несколько условий могут быть описаны через последовательное
применение when. Каждый следующий вызов расширяет или
уточняет предыдущую логику:
const schema = Yup.object({
type: Yup.string(),
country: Yup.string(),
vatNumber: Yup.string()
.when('type', {
is: 'business',
then: schema => schema.required()
})
.when('country', {
is: 'DE',
then: schema => schema.min(9)
})
});
Такой подход формирует каскад условий, где каждое правило применяется независимо, но влияет на итоговую валидность поля.
При работе с объектами Yup передаёт весь контекст значения через
второй аргумент функции when или через this
внутри test.
const schema = Yup.object({
password: Yup.string(),
confirmPassword: Yup.string().when('password', (password, schema) =>
password
? schema.required().oneOf([Yup.ref('password')])
: schema.notRequired()
)
});
Здесь зависимость выражена не только в факте наличия значения, но и в его содержимом.
Когда условий становится слишком много или они не укладываются в
декларативный формат when, применяется метод
test.
const schema = Yup.object({
age: Yup.number(),
hasConsent: Yup.boolean(),
access: Yup.boolean().test(
'access-rule',
'Нет доступа',
function (value) {
const { age, hasConsent } = this.parent;
if (age >= 18 && hasConsent) return true;
if (age < 18 && value === false) return true;
return false;
}
)
});
test позволяет реализовать произвольные логические
выражения, включая вложенные условия, исключения и сложные
бизнес-правила.
В Yup отсутствуют явные логические операторы, но их можно реализовать
через комбинации when и test.
AND-логика:
Yup.string().when(['a', 'b'], {
is: (a, b) => a && b,
then: schema => schema.required()
});
OR-логика:
Yup.string().test('or-rule', '', function (value) {
const { a, b } = this.parent;
return a || b || !!value;
});
Такие конструкции позволяют моделировать практически любые булевы выражения.
Множественные условия часто применяются на уровне вложенных структур:
const schema = Yup.object({
payment: Yup.object({
method: Yup.string(),
cardNumber: Yup.string().when('method', {
is: 'card',
then: schema => schema.required()
}),
paypalEmail: Yup.string().when('method', {
is: 'paypal',
then: schema => schema.email().required()
})
})
});
Здесь выбор метода оплаты полностью меняет требования к структуре вложенного объекта.
Массивы добавляют дополнительный уровень сложности, поскольку условия могут зависеть от индекса или содержимого элементов:
const schema = Yup.object({
users: Yup.array().of(
Yup.object({
type: Yup.string(),
license: Yup.string().when('type', {
is: 'driver',
then: schema => schema.required()
})
})
)
});
Каждый элемент массива получает собственный контекст валидации, что позволяет задавать локальные правила.
При сложной структуре формы поля часто влияют друг на друга одновременно:
const schema = Yup.object({
startDate: Yup.date(),
endDate: Yup.date().when('startDate', (startDate, schema) =>
startDate
? schema.min(startDate)
: schema
)
});
Взаимная зависимость формирует связные ограничения, где корректность одного поля определяется другим.
При использовании нескольких when важно учитывать
порядок их объявления. Каждое последующее условие работает на уже
модифицированной схеме. Это означает, что:
Yup.string()
.when('a', { is: true, then: s => s.required() })
.when('b', { is: true, then: s => s.min(5) });
Финальный результат зависит от последовательного применения всех условий.
Внутри test доступен контекст this,
содержащий:
this.parent — объект текущего уровняthis.options — параметры схемыthis.createError — создание кастомной ошибкиЭто позволяет реализовывать многоуровневую логику без ограничения декларативным синтаксисом.
На практике сложные схемы часто строятся гибридно: when
используется для простых зависимостей, а test — для
объединения нескольких условий, где требуется полный контроль над
логикой проверки.
Такой подход позволяет сохранять читаемость схемы и одновременно поддерживать сложные бизнес-правила без разрыва структуры валидации.