Strict режим в библиотеке Yup управляет поведением
преобразования данных перед валидацией. По умолчанию Yup старается
автоматически привести значения к ожидаемому типу: строки превращаются в
числа, числа — в строки, даты парсятся из текста. Такое поведение
называется casting.
При использовании yupResolver вместе с
React Hook Form это может приводить к ситуациям, когда
данные проходят проверку, хотя фактически имеют неверный тип.
Strict режим отключает автоматическое преобразование типов и заставляет Yup проверять значения «как есть».
Стандартная схема:
import * as yup from 'yup';
const schema = yup.object({
age: yup.number().required(),
});
Проверка:
await schema.validate({
age: '25',
});
Результат:
{ age: 25 }
Хотя значение было строкой, Yup автоматически преобразовал его в число.
Автоматический casting может скрывать ошибки:
await schema.validate({
age: '999abc',
});
Иногда Yup пытается преобразовать значение и возвращает неожиданные результаты.
Другой пример:
const schema = yup.object({
enabled: yup.boolean(),
});
await schema.validate({
enabled: 'true',
});
Результат:
{ enabled: true }
Фактически API получил строку, а не boolean.
В крупных приложениях это приводит к:
Strict режим активируется методом .strict():
const schema = yup.object({
age: yup.number().strict().required(),
});
Теперь:
await schema.validate({
age: '25',
});
Вызывает ошибку:
age must be a `number` type
Преобразование строки в число больше не выполняется.
Strict можно включить глобально:
const schema = yup
.object({
age: yup.number().required(),
name: yup.string().required(),
})
.strict();
Теперь все поля внутри объекта проверяются без casting.
Базовое подключение:
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
const schema = yup
.object({
age: yup.number().strict().required(),
});
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
Если поле age содержит строку "20",
валидация завершится ошибкой.
const schema = yup.object({
age: yup.number(),
});
await schema.validate({
age: '18',
});
Результат:
{ age: 18 }
const schema = yup.object({
age: yup.number().strict(),
});
await schema.validate({
age: '18',
});
Ошибка:
age must be a `number` type
Strict режим особенно полезен для API-валидации.
Например:
const schema = yup.object({
id: yup.number().strict(),
isAdmin: yup.boolean().strict(),
});
Данные:
{
id: "5",
isAdmin: "false"
}
Обе проверки завершатся ошибкой.
Это гарантирует:
Метод transform() перестаёт автоматически вызываться так
же, как в обычном режиме.
Пример:
const schema = yup.string().transform(value => {
return value.trim();
});
В non-strict режиме:
await schema.validate(' hello ');
Результат:
"hello"
Со strict:
const schema = yup
.string()
.transform(value => value.trim())
.strict();
Поведение меняется — Yup перестаёт выполнять часть внутренних преобразований.
Методы:
.trim().lowercase().uppercase()внутри Yup являются transform-операциями.
Пример:
const schema = yup.string().trim().strict();
Проверка:
await schema.validate(' hello ');
Ошибка:
this must be a trimmed string
Yup больше не исправляет значение автоматически, а требует уже корректные данные.
const schema = yup.string().lowercase().strict();
Проверка:
await schema.validate('ADMIN');
Ошибка:
this must be a lowercase string
Без strict значение автоматически стало бы "admin".
const schema = yup.string().uppercase().strict();
Проверка:
await schema.validate('hello');
Ошибка:
this must be a upper case string
Без strict:
const schema = yup.date();
await schema.validate('2025-01-01');
Yup автоматически создаёт объект Date.
Со strict:
const schema = yup.date().strict();
await schema.validate('2025-01-01');
Ошибка:
this must be a `date` type
const schema = yup
.number()
.nullable()
.strict();
Допустимые значения:
null
42
Недопустимо:
"42"
const schema = yup.object({
age: yup.number().strict().optional(),
});
Допустимо:
{}
Недопустимо:
{
age: "20"
}
const schema = yup.object({
age: yup.number().strict().required(),
});
Проверка:
{
age: undefined
}
Ошибка:
age is a required field
Проверка:
{
age: "25"
}
Ошибка:
age must be a `number` type
const schema = yup.object({
profile: yup.object({
age: yup.number().strict(),
}),
});
Проверка:
{
profile: {
age: "30"
}
}
Ошибка будет сгенерирована внутри вложенного объекта.
const schema = yup.array(
yup.number().strict()
);
Проверка:
["1", "2", 3]
Ошибки:
[0] must be a `number` type
[1] must be a `number` type
const schema = yup.mixed().strict();
mixed() допускает любые значения, но strict всё равно
отключает преобразования.
const schema = yup
.number()
.strict()
.test(
'positive',
'Must be positive',
value => value > 0
);
Проверка:
"10"
До кастомного test() выполнение не дойдёт — ошибка типа
возникнет раньше.
Strict режим особенно важен при использовании TypeScript.
Без strict:
const schema = yup.object({
age: yup.number(),
});
TypeScript считает:
age: number
Но runtime допускает:
{
age: "20"
}
Возникает рассинхронизация типов.
Со strict runtime начинает соответствовать TypeScript-описанию.
На backend strict почти всегда предпочтителен.
Причины:
Пример:
const userSchema = yup.object({
id: yup.number().strict(),
email: yup.string().email().strict(),
active: yup.boolean().strict(),
});
Во frontend-формах strict используют осторожно.
Причина:
HTML input почти всегда возвращает строки.
Например:
<input type="number" />
Даже числовой input возвращает:
"25"
Из-за этого strict может постоянно генерировать ошибки.
<input
type="number"
{...register('age', {
valueAsNumber: true,
})}
/>
Теперь React Hook Form передаст число вместо строки.
Strict будет работать корректно.
Для checkbox:
<input
type="checkbox"
{...register('active')}
/>
Boolean обычно приходит корректно, поэтому strict здесь безопасен.
Дополнительное преобразование:
<input
{...register('age', {
setValueAs: value =>
value === '' ? undefined : Number(value),
})}
/>
Теперь schema может быть strict.
Strict никак не зависит от:
onSubmitonBluronChangeallonTouchedОн влияет исключительно на Yup validation pipeline.
Strict можно передать напрямую в validate():
schema.validate(data, {
strict: true,
});
Даже если схема была создана без .strict().
const schema = yup.number();
Проверка:
schema.validate('10', {
strict: true,
});
Ошибка типа будет выброшена.
const schema = yup.object({
id: yup.number().strict(),
name: yup.string(),
});
Только id проверяется без casting.
const schema = yup.number().strict();
await schema.isValid('10');
Результат:
false
Без strict было бы:
true
Метод cast() игнорирует strict-валидацию:
const schema = yup.number().strict();
schema.cast('10');
Результат:
10
Потому что cast() занимается именно преобразованием
типов.
Проверяет корректность.
Преобразует данные.
Strict влияет на validate, но не отменяет назначение
cast.
const schema = yup.object({
age: yup
.number()
.strict()
.required(),
});
const form = useForm({
resolver: yupResolver(schema),
});
Input:
<input
type="number"
{...register('age', {
valueAsNumber: true,
})}
/>
Теперь:
Strict режим особенно полезен:
Strict режим может создавать неудобства:
Наиболее распространённый подход:
Такой подход делает систему предсказуемой и уменьшает количество скрытых ошибок.