Валидация полей в Yup часто выходит за рамки проверки отдельных значений и переходит в область логики, где одно поле напрямую зависит от другого. Подобные зависимости встречаются в формах практически любого уровня сложности: подтверждение пароля, диапазоны дат, условные обязательные поля, динамические правила в зависимости от выбора пользователя.
В Yup зависимости реализуются через контекст объекта схемы. Каждое поле может получить доступ к значениям других полей внутри объекта и на основании этих данных изменять своё поведение.
Ключевой принцип:
валидатор поля может ссылаться на другие поля через контекст объекта или специальные методы Yup
Это позволяет строить не изолированные проверки, а связную модель данных.
refОдин из основных механизмов — использование ref.
import * as Yup from 'yup';
const schema = Yup.object({
password: Yup.string().required('Обязательное поле'),
confirmPassword: Yup.string()
.oneOf([Yup.ref('password')], 'Пароли не совпадают')
.required('Подтвердите пароль'),
});
Здесь:
Yup.ref('password') создаёт ссылку на другое поле
объектаoneOf сравнивает текущее значение с целевымpassword автоматически пересчитывается
валидность confirmPasswordТакой подход используется для простых зависимостей равенства.
whenДля более сложных сценариев используется when, который
позволяет менять схему поля в зависимости от других значений.
const schema = Yup.object({
isCompany: Yup.boolean(),
companyName: Yup.string().when('isCompany', {
is: true,
then: schema => schema.required('Укажите название компании'),
otherwise: schema => schema.notRequired(),
}),
});
when('isCompany') подписывает поле на изменения другого
поляis задаёт условиеthen применяется при истинном условииotherwise — при ложномТаким образом, схема становится динамической.
when поддерживает массив зависимостей:
const schema = Yup.object({
age: Yup.number(),
hasConsent: Yup.boolean(),
guardianConsent: Yup.boolean().when(['age', 'hasConsent'], {
is: (age, hasConsent) => age < 18 && !hasConsent,
then: schema => schema.oneOf([true], 'Требуется согласие опекуна'),
}),
});
Здесь логика строится на комбинации условий, что характерно для реальных бизнес-правил.
test для кастомных зависимостейКогда логика выходит за рамки when, применяется
test, где доступен весь объект:
const schema = Yup.object({
startDate: Yup.date(),
endDate: Yup.date().test(
'date-order',
'Дата окончания должна быть позже начала',
function (value) {
const { startDate } = this.parent;
if (!startDate || !value) return true;
return value > startDate;
}
),
});
this.parent — доступ к объекту текущей формыvalue — текущее значение поляЭтот механизм используется для произвольных зависимостей между любыми полями.
thisВнутри кастомных валидаторов Yup предоставляет контекст:
this.parent — родительский объектthis.path — путь текущего поляthis.createError — создание кастомной ошибкиПример расширенной проверки:
Yup.string().test(
'conditional-length',
'Слишком короткое значение',
function (value) {
const { type } = this.parent;
if (type === 'strict') {
return value && value.length >= 10;
}
return true;
}
);
Здесь поведение поля зависит от другого поля type.
Yup поддерживает вложенные структуры, где зависимости могут быть многоуровневыми.
const schema = Yup.object({
user: Yup.object({
role: Yup.string(),
permissions: Yup.object({
canEdit: Yup.boolean().when('role', {
is: 'admin',
then: schema => schema.oneOf([true]),
}),
}),
}),
});
Особенность:
'role' может относиться к ближайшему объектуuser.rolelazyКогда структура данных зависит от значений, используется
lazy:
const schema = Yup.lazy(value => {
if (value?.type === 'email') {
return Yup.object({
type: Yup.string(),
email: Yup.string().email().required(),
});
}
return Yup.object({
type: Yup.string(),
phone: Yup.string().required(),
});
});
Этот механизм позволяет менять не отдельные поля, а всю структуру объекта в зависимости от данных.
concat и композицию схемИногда зависимости строятся не через условия, а через объединение схем:
const base = Yup.object({
type: Yup.string().required(),
});
const extended = Yup.object({
extra: Yup.string().when('type', {
is: 'advanced',
then: schema => schema.required(),
}),
});
const schema = base.concat(extended);
Композиция позволяет разделять логику на модули, сохраняя зависимости между ними.
Реализуется через ref + oneOf.
Реализуется через test или when.
Реализуется через when.
Yup.object({
phone: Yup.string().when('email', {
is: value => !value,
then: schema => schema.required(),
}),
email: Yup.string().when('phone', {
is: value => !value,
then: schema => schema.required(),
}),
});
Оба поля зависят друг от друга, образуя взаимосвязанный цикл условий.
При построении схем с зависимостями важно учитывать:
when и
reftestlazy полностью пересобирает схему при изменении входных
данныхВ реальных схемах часто сочетаются разные механизмы:
ref для простых сравненийwhen для декларативных условийtest для сложной логикиlazy для динамической структурыТак формируется система валидации, где каждое поле не существует изолированно, а является частью общей модели данных формы.