Библиотека Yup построена вокруг набора базовых схем
(string, number, boolean,
date, array, object,
mixed), каждая из которых представляет собой цепочку
методов валидации. Архитектура позволяет не только комбинировать
встроенные правила, но и расширять поведение типов, добавляя новые
методы, трансформации и пользовательские валидаторы.
Расширение базовых типов применяется в случаях, когда стандартного набора проверок недостаточно: требуется корпоративная бизнес-логика, единые правила валидации для проекта или повторно используемые доменные ограничения.
Основной способ расширения типов в Yup — метод
addMethod. Он позволяет добавлять новые методы к
существующим схемам.
import * as Yup from 'yup';
Yup.addMethod(Yup.string, 'startsWithUppercase', function (message) {
return this.test('starts-with-uppercase', message, function (value) {
if (!value) return true;
return value[0] === value[0].toUpperCase();
});
});
После этого метод становится частью цепочки:
const schema = Yup.string().startsWithUppercase('Должно начинаться с заглавной буквы');
test без дублирования
кода;Строки — наиболее частый кандидат на расширение: форматирование, маски, корпоративные правила.
Yup.addMethod(Yup.string, 'noSpaces', function (message) {
return this.test('no-spaces', message, (value) =>
value ? !/\s/.test(value) : true
);
});
Использование:
Yup.string().noSpaces('Пробелы недопустимы');
Дополнительные примеры расширений:
Числовые типы часто расширяются бизнес-ограничениями:
Yup.addMethod(Yup.number, 'isMultipleOf', function (factor, message) {
return this.test('multiple-of', message, function (value) {
if (value == null) return true;
return value % factor === 0;
});
});
Использование:
Yup.number().isMultipleOf(5, 'Число должно быть кратно 5');
Типичные расширения:
Дата требует специфической логики, особенно в доменных моделях:
Yup.addMethod(Yup.date, 'isWeekday', function (message) {
return this.test('is-weekday', message, function (value) {
if (!value) return true;
const day = value.getDay();
return day !== 0 && day !== 6;
});
});
Массивы расширяются для контроля структуры:
Yup.addMethod(Yup.array, 'minUniqueItems', function (message) {
return this.test('min-unique-items', message, function (value) {
if (!value) return true;
const unique = new Set(value);
return unique.size === value.length;
});
});
Базовый механизм расширения в Yup — метод test. Он
используется внутри addMethod, но также может применяться
напрямую.
Yup.string().test(
'no-admin',
'Недопустимое значение',
(value) => value !== 'admin'
);
Функция-валидатор получает объект контекста:
value — текущее значение;path — путь в объекте;createError — генерация ошибки;parent — родительский объект;options — параметры схемы.Пример с динамической ошибкой:
Yup.string().test(
'min-length-dynamic',
function (value) {
const min = this.options.context?.minLength || 0;
return value && value.length >= min
? true
: this.createError({ message: `Минимум ${min} символов` });
}
);
Тип mixed является базовым уровнем абстракции, от
которого наследуются все остальные схемы. Его расширение позволяет
создавать универсальные валидаторы.
Yup.addMethod(Yup.mixed, 'isRequiredIf', function (condition, message) {
return this.test('is-required-if', message, function (value) {
const shouldBeRequired = condition(this.parent);
if (!shouldBeRequired) return true;
return value != null;
});
});
Применение:
Yup.object({
email: Yup.string().email(),
phone: Yup.string().isRequiredIf(
(parent) => !parent.email,
'Телефон обязателен, если нет email'
)
});
Расширения часто комбинируются в наборы правил:
const commonStringRules = (schema) =>
schema.trim().min(3).max(50);
const usernameSchema = commonStringRules(
Yup.string()
).noSpaces().startsWithUppercase();
Такой подход позволяет:
Хотя when не является расширением, он часто используется
вместе с кастомными методами:
Yup.string().when('role', {
is: 'admin',
then: (schema) => schema.required().min(10),
otherwise: (schema) => schema.min(3)
});
В связке с расширенными методами:
Yup.string()
.noSpaces()
.when('strict', {
is: true,
then: (schema) => schema.startsWithUppercase()
});
Метод transform позволяет изменять входные данные до
валидации:
Yup.addMethod(Yup.string, 'normalizeSpaces', function () {
return this.transform((value) =>
typeof value === 'string' ? value.replace(/\s+/g, ' ') : value
);
});
Использование:
Yup.string().normalizeSpaces().trim();
Расширения часто сопровождаются настройкой сообщений:
Yup.setLocale({
mixed: {
required: 'Поле обязательно'
},
string: {
min: 'Слишком короткое значение'
}
});
При кастомных методах важно передавать сообщения через параметры
test, а не хардкодить их внутри логики.
При использовании TypeScript необходимо расширять интерфейсы схем:
import 'yup';
declare module 'yup' {
interface StringSchema {
noSpaces(message?: string): this;
startsWithUppercase(message?: string): this;
}
}
Без этого расширенные методы будут недоступны в типах, несмотря на их наличие в runtime.
const createEmailSchema = () =>
Yup.string()
.email()
.required()
.trim();
const baseString = Yup.string().trim().min(2);
const username = baseString.noSpaces().startsWithUppercase();
const isPositiveId = Yup.number().integer().positive();
const isExternalId = isPositiveId.isMultipleOf(3, 'Некорректный ID');
Объекты позволяют внедрять сложную структуру:
Yup.addMethod(Yup.object, 'withTimestamps', function () {
return this.shape({
createdAt: Yup.date().required(),
updatedAt: Yup.date().required()
});
});