Валидация в Yup состоит не только из проверок. Перед запуском правил
библиотека проходит через цепочку преобразований данных — трансформаций
(transform). Именно этот этап определяет, какое значение
попадёт в валидаторы (required, min,
matches, test и другие).
Полный порядок обработки выглядит так:
transformdefaulttests)Ключевая особенность Yup — проверки работают уже над преобразованным значением, а не над оригинальным вводом.
Пример:
import * as yup from 'yup';
const schema = yup
.string()
.trim()
.lowercase()
.min(3);
schema.validate(' HELLO ');
Порядок будет следующим:
1. Исходное значение:
" HELLO "
2. trim():
"HELLO"
3. lowercase():
"hello"
4. min(3):
проверка проходит
Результат:
"hello"
Каждый тип Yup имеет собственные встроенные преобразования.
yup.string()
Автоматически приводит значение к строке.
await yup.string().validate(123);
Результат:
"123"
await yup.number().validate("42");
Результат:
42
Yup пытается выполнить:
Number(value)
Если преобразование невозможно:
await yup.number().validate("abc");
Возникнет ошибка:
this must be a `number` type
await yup.boolean().validate("true");
Результат:
true
Поддерживаются значения:
"true"
"false"
"1"
"0"
1
0
await yup.date().validate("2025-01-01");
Результат:
Date
Yup вызывает:
new Date(value)
Пользовательские трансформации выполняются после базового преобразования типа.
Пример:
const schema = yup
.number()
.transform(value => value * 2);
await schema.validate("5");
Порядок:
1. "5"
2. number() → 5
3. transform() → 10
Результат:
10
Метод принимает функцию:
transform((currentValue, originalValue) => {})
Значение после предыдущих трансформаций.
Исходное значение до любых преобразований.
const schema = yup
.number()
.transform((value, originalValue) => {
console.log(value);
console.log(originalValue);
return value;
});
await schema.validate("25");
Вывод:
25
"25"
Трансформации выполняются сверху вниз.
const schema = yup
.string()
.transform(value => value.trim())
.transform(value => value.toUpperCase())
.transform(value => `[${value}]`);
await schema.validate(" hello ");
Порядок:
1. " hello "
2. "hello"
3. "HELLO"
4. "[HELLO]"
Результат:
"[HELLO]"
Методы:
trim()lowercase()uppercase()camelCase()ensure()compact()являются обычными трансформациями внутри цепочки.
Пример:
const schema = yup
.string()
.trim()
.uppercase();
await schema.validate(" test ");
Результат:
"TEST"
Проверки получают уже изменённое значение.
const schema = yup
.string()
.trim()
.min(5);
await schema.validate(" hello ");
Порядок:
1. trim() → "hello"
2. min(5)
Без trim() длина была бы 11 символов.
Трансформации выполняются раньше required().
const schema = yup
.string()
.transform(value => value === '' ? undefined : value)
.required();
Проверка:
await schema.validate('');
Порядок:
1. "" → undefined
2. required() → ошибка
const phoneSchema = yup
.string()
.transform(value => value.replace(/\D/g, ''));
await phoneSchema.validate('+7 (777) 123-45-67');
Результат:
"77771234567"
const schema = yup
.string()
.trim()
.lowercase()
.email();
await schema.validate(' ADMIN@SITE.COM ');
Результат:
"admin@site.com"
Это важный механизм управления логикой валидации.
const schema = yup
.number()
.transform(value => isNaN(value) ? undefined : value);
Теперь:
await schema.validate('abc');
приведёт к:
undefined
Далее поведение зависит от:
required()nullable()optional()default() применяется после трансформаций.
const schema = yup
.string()
.transform(value => value || undefined)
.default('guest');
Проверка:
schema.cast('');
Порядок:
1. "" → undefined
2. default() → "guest"
Результат:
"guest"
Метод cast() запускает трансформации без выполнения
validation tests.
const schema = yup
.string()
.trim()
.lowercase();
schema.cast(' HELLO ');
Результат:
"hello"
Но:
schema.cast(123);
тоже выполнит преобразование:
"123"
Только преобразование:
transform → result
Полный цикл:
transform → tests → result/error
Режим strict() запрещает автоматические
преобразования.
const schema = yup
.number()
.strict();
await schema.validate("42");
Ошибка:
this must be a `number` type
Без strict() строка была бы преобразована в число.
В строгом режиме не работают даже пользовательские трансформации.
const schema = yup
.string()
.transform(value => value.trim())
.strict();
await schema.validate(' hello ');
Результат:
" hello "
Трансформация пропущена.
const schema = yup
.string()
.nullable()
.transform(value => value === '' ? null : value);
Проверка:
await schema.validate('');
Порядок:
1. "" → null
2. nullable() разрешает null
3. validation проходит
Трансформации выполняются отдельно для каждого поля.
const schema = yup.object({
name: yup.string().trim(),
age: yup.number()
});
Проверка:
await schema.validate({
name: ' Alex ',
age: '25'
});
Результат:
{
name: 'Alex',
age: 25
}
Для объекта Yup:
const schema = yup.array(
yup.string().trim().uppercase()
);
await schema.validate([
' hello ',
' world '
]);
Результат:
['HELLO', 'WORLD']
const schema = yup
.array()
.compact();
Удаляются:
false
null
0
""
undefined
Пример:
await schema.validate([
'A',
'',
'B',
null
]);
Результат:
['A', 'B']
lazy() создаёт схему динамически после получения
значения.
const schema = yup.lazy(value => {
if (typeof value === 'string') {
return yup.string().trim();
}
return yup.number();
});
Порядок:
1. Получение значения
2. Выбор схемы
3. Выполнение transform()
4. Валидация
const schema = yup
.string()
.trim()
.test(
'is-admin',
'Not admin',
value => value === 'admin'
);
Проверка:
await schema.validate(' admin ');
test() получает:
"admin"
Плохой пример:
.transform(value => {
if (value.length < 5) {
throw new Error();
}
return value;
})
Трансформации должны:
Проверки должны выполняться через:
minmaxmatchestestХорошая трансформация должна давать одинаковый результат при повторном вызове.
Хорошо:
value => value.trim()
Плохо:
value => value + Math.random()
Если внутри transform возникает исключение:
.transform(() => {
throw new Error('Fail');
})
валидация завершается аварийно.
Лучше возвращать безопасные значения:
.transform(value => {
try {
return JSON.parse(value);
} catch {
return undefined;
}
})
Нельзя:
.transform(async value => {})
transform() всегда синхронный.
Асинхронность допускается только внутри test().
После завершения всех transform Yup запускает проверки.
const schema = yup
.string()
.trim()
.test('a', 'A', value => true)
.test('b', 'B', value => true);
Порядок:
1. transform
2. test a
3. test b
validate(value, {
abortEarly: true
});
Даже при ранней остановке:
const schema = yup.object({
type: yup.string(),
value: yup.string().when('type', {
is: 'email',
then: schema =>
schema
.trim()
.lowercase()
.email()
})
});
Порядок:
1. Определение условия when()
2. Построение схемы
3. transform()
4. validation tests
Пример сложной схемы:
const schema = yup
.string()
.transform(value => value.trim())
.transform(value => value.toLowerCase())
.transform(value => value || undefined)
.default('guest')
.required()
.min(3);
Проверка:
await schema.validate(' ADMIN ');
Полный порядок:
1. Исходное значение:
" ADMIN "
2. trim():
"ADMIN"
3. toLowerCase():
"admin"
4. value || undefined:
"admin"
5. default():
не применяется
6. required():
проходит
7. min(3):
проходит
8. Финальный результат:
"admin"
Yup строит конвейер обработки:
input
↓
type casting
↓
transform chain
↓
default values
↓
validation tests
↓
output
Понимание этой последовательности критически важно для: