Валидационные схемы в Yup обладают модульной
природой: каждая схема представляет собой независимый набор правил,
который можно комбинировать с другими схемами. Одним из ключевых
инструментов композиции является метод concat, позволяющий
объединять две схемы в одну с последующим наследованием и расширением
правил валидации.
Метод concat выполняет объединение двух совместимых
схем, формируя новую схему, содержащую правила обеих исходных. При этом
одна из схем выступает базовой, а вторая — расширяющей.
import * as Yup from 'yup';
const baseSchema = Yup.string().min(3, 'Минимум 3 символа');
const extendedSchema = baseSchema.concat(
Yup.string().max(10, 'Максимум 10 символов')
);
В результате extendedSchema будет проверять строку сразу
по двум ограничениям: минимальной и максимальной длине.
Одним из ключевых ограничений concat является
необходимость совпадения типов объединяемых схем. Нельзя объединить,
например, строковую и числовую схему.
Корректные примеры:
Yup.string().concat(Yup.string().required());
Yup.number().concat(Yup.number().positive());
Некорректный пример:
Yup.string().concat(Yup.number()); // приведёт к ошибке
Это связано с тем, что Yup строго разделяет типы валидируемых значений и не выполняет автоматическое приведение.
При объединении схем через concat важен порядок: правила
базовой схемы применяются первыми, затем добавляются правила расширяющей
схемы.
const schema = Yup.string()
.min(2, 'base min')
.concat(
Yup.string().min(5, 'extended min')
);
В данном случае итоговая схема будет требовать минимум 5 символов, поскольку более строгие ограничения перекрывают слабые.
При конфликте правил Yup не складывает их арифметически, а фактически применяет наиболее строгие или переопределяющие условия. Это особенно заметно при работе с одинаковыми валидаторами.
Пример:
const schema = Yup.number()
.min(1, 'base min')
.concat(
Yup.number().min(10, 'extended min')
);
Фактически итоговое ограничение будет min(10), так как
оно более строгое.
Наиболее практическое применение concat проявляется при
работе с объектами. Объектные схемы позволяют поэтапно собирать
структуру данных.
const baseUserSchema = Yup.object({
id: Yup.number().required(),
createdAt: Yup.date().required()
});
const profileExtension = Yup.object({
name: Yup.string().required(),
age: Yup.number().min(18)
});
const fullSchema = baseUserSchema.concat(profileExtension);
В результате получается единая схема, содержащая все поля обоих объектов.
Если в двух объединяемых объектных схемах присутствуют одинаковые ключи, поведение становится зависимым от порядка объединения. Значение схемы из второй (расширяющей) структуры заменяет первую.
const base = Yup.object({
role: Yup.string().oneOf(['user', 'admin'])
});
const override = Yup.object({
role: Yup.string().oneOf(['admin'])
});
const schema = base.concat(override);
В данном случае поле role будет ограничено только
значением 'admin'.
Метод поддерживает последовательное расширение, что позволяет строить многоуровневые схемы.
const schema = Yup.string()
.min(3)
.concat(Yup.string().max(20))
.concat(Yup.string().matches(/^[a-z]+$/));
Такая цепочка часто используется для построения гибких библиотечных или доменных схем.
concat работает не только с базовыми типами, но и с
пользовательскими валидаторами, созданными через test.
const base = Yup.string().test(
'no-spaces',
'Не должно содержать пробелов',
value => !/\s/.test(value)
);
const extended = Yup.string().min(5);
const schema = base.concat(extended);
Таким образом кастомная логика сохраняется и дополняется стандартными ограничениями.
Несмотря на гибкость, механизм имеет ряд особенностей:
В крупных приложениях concat часто используется для
разбиения валидации на слои:
const baseProduct = Yup.object({
id: Yup.number().required(),
title: Yup.string().required()
});
const pricingExtension = Yup.object({
price: Yup.number().positive().required()
});
const adminExtension = Yup.object({
internalCode: Yup.string()
});
const productSchema = baseProduct
.concat(pricingExtension)
.concat(adminExtension);
Такой подход снижает дублирование и упрощает поддержку валидации.
При объединении схем с разными модификаторами (nullable,
optional) итоговое поведение зависит от более строгой
конфигурации.
const a = Yup.string().nullable();
const b = Yup.string().required();
const schema = a.concat(b);
Результатом будет схема с обязательным значением, так как
required имеет приоритет над nullable в
контексте проверки.
Если схемы содержат transform, то при объединении они не
заменяют друг друга автоматически. Трансформации применяются в порядке
выполнения схемы, что может приводить к цепочному изменению
значения.
const trimSchema = Yup.string().transform(v => v.trim());
const lowerSchema = Yup.string().transform(v => v.toLowerCase());
const schema = trimSchema.concat(lowerSchema);
В этом случае сначала выполняется trim, затем
toLowerCase.
Использование concat позволяет строить слоистую
архитектуру валидации, где:
Такой подход особенно полезен при разработке сложных форм, где один и тот же набор данных валидируется в разных контекстах с минимальными изменениями логики.