Валидация в Yup строится вокруг схем, которые легко комбинируются и повторно используются. Основная идея заключается в том, что любая схема может быть выделена в самостоятельный модуль и применена в разных контекстах без дублирования логики.
Переиспользование достигается через несколько механизмов:
concat)yup-схемы)addMethodobject().shape с общими
фрагментами)when)lazy)Один из самых практичных подходов — создание функций, возвращающих готовые схемы.
import * as yup fr om 'yup';
export const emailValidator = () =>
yup
.string()
.trim()
.email('Некорректный email')
.required('Email обязателен');
export const passwordValidator = () =>
yup
.string()
.min(8, 'Минимум 8 символов')
.matches(/[A-Z]/, 'Должна быть хотя бы одна заглавная буква')
.matches(/[0-9]/, 'Должна быть хотя бы одна цифра')
.required('Пароль обязателен');
Такой подход позволяет:
Использование:
const schema = yup.object({
email: emailValidator(),
password: passwordValidator(),
});
concat позволяет объединять две схемы одного типа в
одну.
const baseString = yup.string().trim();
const nameSchema = baseString.concat(
yup.string().min(2, 'Слишком короткое имя')
);
Это особенно полезно, когда есть:
При работе с объектами часто выделяется базовая структура.
const timestamps = {
createdAt: yup.date().required(),
updatedAt: yup.date().required(),
};
const baseUserSchema = yup.object({
id: yup.number().required(),
...timestamps,
});
Дальнейшее расширение:
const adminSchema = baseUserSchema.shape({
role: yup.string().oneOf(['admin']).required(),
});
Такой подход формирует иерархию схем, где базовый уровень не дублируется.
addMethod позволяет добавлять собственные валидаторы ко
всем схемам определённого типа.
import * as yup from 'yup';
yup.addMethod(yup.string, 'onlyDigits', function (message) {
return this.matches(/^\d+$/, message || 'Только цифры');
});
Использование:
const schema = yup.object({
phone: yup.string().onlyDigits().required(),
});
Важно учитывать:
Для сложной логики удобно использовать параметры.
const minLengthString = (min) =>
yup.string().min(min, `Минимум ${min} символов`).required();
const usernameSchema = minLengthString(3);
Более сложный пример:
const rangeNumber = (min, max) =>
yup
.number()
.min(min, `Минимум ${min}`)
.max(max, `Максимум ${max}`)
.required();
Такой стиль делает схемы:
Метод when позволяет включать повторно используемые
правила в зависимости от контекста.
const optionalEmail = yup.string().when('subscribe', {
is: true,
then: (schema) => emailValidator(),
otherwise: (schema) => schema.notRequired(),
});
Здесь уже существующий валидатор внедряется в условную логику без дублирования.
lazy применяется, когда структура данных зависит от
значения.
const dynamicSchema = yup.lazy((value) => {
if (typeof value === 'string') {
return yup.string().min(3);
}
if (typeof value === 'number') {
return yup.number().min(0);
}
return yup.mixed();
});
Это позволяет переиспользовать логику для непредсказуемых форматов данных.
В крупных приложениях схемы начинают рассматриваться как набор примитивов:
emailValidatorphoneValidatoridValidatortimestampFieldspaginationSchemaПример композиции:
const paginationSchema = yup.object({
page: yup.number().min(1).required(),
lim it: yup.number().min(1).max(100).required(),
});
const searchSchema = paginationSchema.shape({
query: yup.string().min(1).required(),
});
Практика переиспользования усиливается при доменной организации:
/validation
/common
stringSchemas.js
numberSchemas.js
/user
userSchemas.js
/order
orderSchemas.js
Общие схемы становятся зависимостями для доменных модулей, что снижает связанность.
Частичные схемы позволяют собирать объект из независимых блоков.
const addressPart = {
city: yup.string().required(),
street: yup.string().required(),
};
const userWithAddress = yup.object({
name: yup.string().required(),
...addressPart,
});
При росте проекта такие блоки становятся стандартными строительными элементами.
Переиспользуемые валидаторы часто отражают бизнес-логику, а не технические ограничения.
export const adultAgeValidator = () =>
yup
.number()
.min(18, 'Требуется совершеннолетие')
.max(120, 'Некорректный возраст');
Использование в разных схемах:
const registrationSchema = yup.object({
age: adultAgeValidator(),
});
Сложные структуры строятся из простых:
const baseProduct = yup.object({
id: yup.number().required(),
title: yup.string().required(),
});
const pricedProduct = baseProduct.shape({
price: yup.number().positive().required(),
});
const discountedProduct = pricedProduct.shape({
discount: yup.number().min(0).max(100),
});
Так формируется цепочка расширений без повторения базовых правил.
При масштабировании важно соблюдать несколько технических принципов:
addMethod расширенияТакая структура позволяет сохранять предсказуемость при росте кодовой базы и уменьшает вероятность расхождений в правилах валидации между модулями.