В библиотеке Yup механизм кастомной валидации строится вокруг метода
test, позволяющего внедрять собственные правила проверки
данных. Центральной частью этого механизма является формирование
сообщений об ошибках, которые должны быть предсказуемыми, контекстными и
согласованными со структурой схемы.
Кастомный валидатор в Yup задаётся через test, который
принимает имя проверки и функцию-валидатор:
Yup.string().test(
'is-even-length',
'Длина строки должна быть чётной',
(value) => {
if (!value) return true;
return value.length % 2 === 0;
}
);
В этом варианте строка 'Длина строки должна быть чётной'
является статическим сообщением об ошибке, которое возвращается при
провале проверки.
Однако такой подход ограничен: сообщение не зависит от входных данных и контекста.
Функция-валидатор может возвращать не только true/false,
но и управлять ошибкой через this.createError:
Yup.string().test(
'min-words',
function (value) {
const min = 3;
if (!value) return true;
const words = value.trim().split(/\s+/);
if (words.length < min) {
return this.createError({
message: `Минимальное количество слов: ${min}. Текущее: ${words.length}`,
});
}
return true;
}
);
Здесь используется контекст this, который предоставляет
доступ к:
path — путь к полю в объектеparent — родительский объектoriginalValue — исходное значениеcreateError — фабрика ошибокТакой подход позволяет формировать сообщения, зависящие от состояния данных.
Кастомные ошибки часто строятся на основе контекста схемы:
Yup.number().test(
'max-by-role',
function (value) {
const { role } = this.parent;
const max = role === 'admin' ? 1000 : 100;
if (value > max) {
return this.createError({
message: `Превышено допустимое значение (${max}) для роли ${role}`,
});
}
return true;
}
);
Использование this.parent позволяет учитывать соседние
поля объекта при формировании ошибки.
optionscreateError поддерживает дополнительные поля, влияющие
на структуру ошибки:
return this.createError({
message: 'Некорректное значение',
path: this.path,
});
path задаёт конкретное поле, к которому привязывается
ошибка. Это важно при вложенных объектах, где ошибка может быть
зарегистрирована на уровне глубже, чем сама схема.
В Yup существует два подхода:
return false;
или
return 'Ошибка';
В этом случае Yup автоматически оборачивает результат в
ValidationError.
return this.createError({ message: 'Ошибка' });
Этот способ предпочтителен при сложной логике, так как позволяет:
pathВ одном валидаторе может быть несколько причин ошибки:
Yup.string().test(
'complex-check',
function (value) {
if (!value) {
return this.createError({ message: 'Значение обязательно' });
}
if (value.length < 5) {
return this.createError({ message: 'Минимальная длина 5 символов' });
}
if (!/^[a-z]+$/.test(value)) {
return this.createError({ message: 'Допустимы только латинские буквы' });
}
return true;
}
);
Важно учитывать, что Yup по умолчанию останавливается на первой
ошибке, если не изменён режим abortEarly.
При abortEarly: true возвращается первая ошибка:
Yup.string().test('t', function (value) {
if (!value) return this.createError({ message: 'A' });
if (value.length < 5) return this.createError({ message: 'B' });
if (!value.includes('x')) return this.createError({ message: 'C' });
return true;
});
При abortEarly: false могут быть собраны все ошибки,
если они возникают на разных уровнях схемы.
Кастомные валидаторы могут быть асинхронными:
Yup.string().test(
'exists',
async function (value) {
const exists = await checkUser(value);
if (!exists) {
return this.createError({
message: 'Пользователь не найден',
});
}
return true;
}
);
Асинхронная функция должна возвращать
Promise<boolean | ValidationError>.
При генерации ошибки Yup формирует объект:
message — текст ошибкиpath — путь к полюvalue — значениеtype — тип валидатораinner — вложенные ошибки (для объектов и массивов)Кастомные валидаторы влияют на message,
path и type, если они заданы вручную.
Тип ошибки может быть задан явно:
return this.createError({
message: 'Недопустимое значение',
type: 'custom-range-error',
});
Это позволяет различать ошибки на уровне обработки формы или API.
Часто сообщения об ошибках выносятся в словари:
const messages = {
required: 'Поле обязательно',
min: (min) => `Минимум ${min} символов`,
};
Yup.string().test('min', function (value) {
if (value.length < 3) {
return this.createError({
message: messages.min(3),
});
}
return true;
});
Такой подход упрощает поддержку мультиязычных интерфейсов.
Глобальные сообщения Yup можно переопределять:
Yup.setLocale({
mixed: {
required: 'Обязательное поле',
},
});
Однако кастомные валидаторы имеют приоритет, так как
createError всегда перекрывает локализацию.
При работе с объектами важно корректно задавать
path:
Yup.object({
user: Yup.object({
age: Yup.number().test(
'adult',
function (value) {
if (value < 18) {
return this.createError({
message: 'Возраст должен быть 18+',
path: 'user.age',
});
}
return true;
}
),
}),
});
Без явного path Yup автоматически определяет путь, но в
сложных схемах это может привести к неточной привязке ошибки.
В Yup валидаторы выполняются в порядке объявления:
Yup.string()
.min(5, 'Слишком коротко')
.test('custom', function (value) {
return this.createError({ message: 'Кастомная ошибка' });
});
Если срабатывает кастомный test, он полностью
перекрывает предыдущие сообщения.
Контекст позволяет различать трансформированное и исходное значение:
Yup.string()
.transform((value) => value.trim())
.test('compare', function (value) {
if (this.originalValue !== value) {
return this.createError({
message: 'Значение было изменено перед валидацией',
});
}
return true;
});
Это полезно при сложных цепочках transform.
Механизм формирования сообщений об ошибках в кастомных проверках Yup опирается на три уровня контроля:
true/falsethis.createError для полного управления
структурой ошибкиЭти уровни позволяют строить как простые проверки, так и сложные системы валидации с контекстными, локализованными и структурированными сообщениями.