Валидационная модель Yup строится на декларативном описании формы
данных, где каждое поле явно классифицируется как обязательное или
необязательное. При интеграции с YupResolver (чаще всего в связке с
React Hook Form через @hookform/resolvers/yup) эта
классификация напрямую влияет на формирование ошибок, типизацию и
поведение формы при сабмите.
Обязательность в Yup задаётся через метод required(),
который меняет поведение схемы и заставляет валидатор считать отсутствие
значения ошибкой.
import * as yup from "yup";
const schema = yup.object({
email: yup.string().email().required(),
});
Поведение required() в контексте Yup:
undefined трактуется как отсутствие значения"" также считается невалидной для
строковых полейnull не считается валидным значением, если явно не
разрешёнВнутренне Yup формирует специальное условие, которое проверяется
после базовой валидации типа. Это означает, что порядок вызовов методов
имеет значение: сначала выполняется типовая проверка
(string, number, date), затем
ограничения (email, min, max), и
только потом — required().
Пример с кастомным сообщением:
yup.string().required("Поле обязательно для заполнения");
Необязательные поля в Yup задаются не отсутствием
required(), а явным указанием notRequired()
либо отсутствием обязательных ограничений в цепочке валидации.
const schema = yup.object({
nickname: yup.string().notRequired(),
});
Особенность Yup заключается в том, что поле считается необязательным
по умолчанию, если отсутствует required(). Однако поведение
становится менее очевидным при добавлении других правил:
yup.string().min(3);
В этом случае поле остаётся необязательным, но при наличии значения оно должно соответствовать правилам.
notRequired, nullable и отсутствием
значенияnotRequired() — поле может отсутствовать (undefined
допускается)nullable() — поле может принимать null как
валидное значениеrequired() — поведение по умолчанию,
эквивалентно необязательности, но без явной семантикиКомбинации:
yup.string().nullable().notRequired();
Такое определение допускает undefined, null
и строку.
В контексте YupResolver критически важно различать три состояния отсутствия данных:
undefined — поле не задано в объекте формыnull — явно переданное пустое значение"" — пустая строка, часто возникающая в
HTML-инпутахДля строковых схем:
required() блокирует ""nullable() разрешает null, но не влияет на
""transform() часто используется для нормализации пустых
строк в undefinedПример нормализации:
yup.string().transform((value, originalValue) =>
originalValue === "" ? undefined : value
);
YupResolver преобразует Yup-схему в функцию валидации, которая возвращает объект ошибок в формате, совместимом с React Hook Form.
import { yupResolver } from "@hookform/resolvers/yup";
Внутренний процесс включает:
schema.validate(...)Обязательные поля влияют на формирование
ValidationError, который затем маппится в
formState.errors.
Если поле отсутствует:
required() создаётся ошибка с ключом поляrequired() поле игнорируется, если не
нарушены другие правилаВ React Hook Form поведение required/optional тесно связано с
defaultValues.
const form = useForm({
resolver: yupResolver(schema),
defaultValues: {
email: "",
},
});
Если defaultValues.email задан как пустая строка:
required() поле остаётся валидным при отсутствии
других ограниченийrequired() пустая строка становится ошибкойЕсли значение undefined:
required()При использовании TypeScript YupSchema влияет на вывод типов, однако Yup сам по себе не всегда строго отражает обязательность в типах.
Пример:
const schema = yup.object({
email: yup.string().required(),
phone: yup.string().notRequired(),
});
Фактическая типизация может допускать:
email: string | undefined на уровне TS-инференсаInferType<typeof schema>В связке с YupResolver типы формы могут быть шире, чем логическая обязательность.
Yup поддерживает динамическую обязательность через when,
что критично для форм с зависимыми полями.
yup.object({
paymentMethod: yup.string(),
cardNumber: yup.string().when("paymentMethod", {
is: "card",
then: (schema) => schema.required(),
otherwise: (schema) => schema.notRequired(),
}),
});
Здесь обязательность вычисляется на этапе валидации, а YupResolver просто транслирует результат.
Для объектов:
yup.object({
profile: yup.object({
name: yup.string().required(),
}).required(),
});
Важно различать:
object().required() — объект должен существоватьДля массивов:
yup.array().of(
yup.string().required()
).required();
Здесь:
YupResolver формирует объект ошибок с вложенной структурой:
{
email: {
type: "required",
message: "Field is required"
}
}
Для вложенных объектов:
{
profile: {
name: {
type: "required",
message: "Required"
}
}
}
Обязательность влияет на type, чаще всего:
requiredtypeErrornullableКомбинации правил создают тонкие эффекты:
yup.string().nullable().required();
В таком случае:
null разрешён на уровне типаrequired() всё равно может считать null
ошибкой, если не настроена трансформацияКорректная интерпретация зависит от порядка и наличия
transform().
Для унификации поведения часто применяется явная нормализация входных данных:
const schema = yup.object({
comment: yup
.string()
.transform(v => (v === "" ? undefined : v))
.notRequired(),
});
Такая модель устраняет неоднозначность между отсутствием значения и пустым вводом, что критично при работе YupResolver в формах с множеством текстовых полей.