Валидация объектов в Yup строится на декларативном описании структуры данных, где каждое поле получает собственные правила проверки. Для этого используется метод object(), внутри которого формируется схема с помощью shape(). Именно shape определяет «форму» объекта — набор ключей, их типы и ограничения.
В основе подхода лежит идея строгого соответствия структуры данных заранее заданному контракту. Это особенно важно при работе с формами, API-ответами и конфигурационными объектами, где любая несогласованность приводит к ошибкам выполнения или некорректной обработке данных.
Метод object() создаёт валидатор для объектов, а shape() задаёт структуру полей внутри него. Каждый ключ в shape соответствует полю объекта, а значение — схеме валидации этого поля.
import * as Yup from 'yup';
const schema = Yup.object().shape({
name: Yup.string().required(),
age: Yup.number().min(18),
email: Yup.string().email()
});
В этом примере объект должен содержать три поля: name, age и email. Каждое поле проверяется независимо, но в рамках единой структуры.
Ключевой принцип: shape не просто перечисляет поля, а фиксирует контракт объекта, который должен быть соблюдён целиком.
Использование shape позволяет рассматривать объект как строго типизированную структуру. Даже в динамическом JavaScript появляется поведение, близкое к статической типизации: отсутствие поля, лишние ключи или несоответствие типов могут быть выявлены заранее.
const userSchema = Yup.object().shape({
id: Yup.number().required(),
username: Yup.string().min(3).max(30),
isActive: Yup.boolean()
});
В данном случае объект считается валидным только при соблюдении всех условий:
Любое отклонение нарушает форму объекта.
Одним из ключевых преимуществ Yup является возможность описывать вложенные структуры. shape может содержать внутри себя другие object-схемы, формируя древовидную модель данных.
const schema = Yup.object().shape({
user: Yup.object().shape({
name: Yup.string().required(),
profile: Yup.object().shape({
age: Yup.number().min(0),
city: Yup.string()
})
})
});
Такая конструкция позволяет описывать сложные доменные модели, где данные организованы иерархически.
Особенность: каждая вложенная схема полностью независима, но участвует в общей проверке объекта.
Shape не требует обязательного описания всех возможных полей объекта. Можно валидировать только часть структуры, игнорируя дополнительные данные.
const schema = Yup.object().shape({
title: Yup.string().required()
});
Объект может содержать любые дополнительные ключи, если не включено строгое ограничение. Это позволяет использовать схему как фильтр для ключевых данных, а не как абсолютный контракт.
Для более строгого контроля используется метод strict() и конфигурации, ограничивающие лишние поля.
По умолчанию поля в shape считаются необязательными, если явно не указано required(). Это позволяет описывать гибкие структуры, где часть данных может отсутствовать.
const schema = Yup.object().shape({
phone: Yup.string(),
address: Yup.string().required()
});
В этом случае объект может быть валидным даже без phone, но отсутствие address приведёт к ошибке.
Shape используется не только для проверки, но и для приведения данных к нужному виду через transform().
const schema = Yup.object().shape({
price: Yup.number()
.transform((value, originalValue) => {
return typeof originalValue === 'string'
? parseFloat(originalValue)
: value;
})
});
Здесь shape становится частью механизма нормализации входных данных перед их валидацией.
Одной из мощных возможностей является зависимость полей друг от друга. shape позволяет комбинировать схему с when() для построения условной логики.
const schema = Yup.object().shape({
isCompany: Yup.boolean(),
companyName: Yup.string().when('isCompany', {
is: true,
then: schema => schema.required(),
otherwise: schema => schema.notRequired()
})
});
Форма объекта динамически изменяется в зависимости от значений внутри него, но структура shape остаётся центральным описанием.
shape может формироваться программно, что позволяет строить схемы на основе конфигураций или метаданных.
const fields = {
firstName: Yup.string(),
lastName: Yup.string(),
age: Yup.number()
};
const schema = Yup.object().shape(fields);
Такой подход широко используется в генерации форм, где структура неизвестна заранее.
Схемы можно расширять, создавая новые варианты объектов на основе существующих.
const baseSchema = Yup.object().shape({
id: Yup.number().required()
});
const extendedSchema = baseSchema.shape({
name: Yup.string().required()
});
В результате формируется новая форма объекта, включающая базовые и дополнительные поля.
При необходимости можно полностью зафиксировать форму объекта, исключив любые дополнительные ключи.
const schema = Yup.object()
.shape({
name: Yup.string(),
age: Yup.number()
})
.noUnknown(true);
В этом случае объект обязан строго соответствовать описанной структуре shape, без лишних свойств.
Каждое поле внутри shape генерирует собственное сообщение об ошибке. Ошибки агрегируются в структурированном виде, повторяя структуру объекта.
{
name: "Name is required",
age: "Must be a number"
}
Это позволяет легко интегрировать результат в формы и UI-компоненты, отображая ошибки рядом с соответствующими полями.
Shape поддерживает композицию, позволяя объединять несколько логических блоков в одну структуру.
const address = {
city: Yup.string(),
street: Yup.string()
};
const schema = Yup.object().shape({
name: Yup.string(),
address: Yup.object().shape(address)
});
Такой подход снижает дублирование и упрощает поддержку сложных моделей данных.
Shape формирует предсказуемую структуру данных, что особенно важно при интеграции с внешними API. Любое несоответствие между ожидаемой и фактической формой объекта фиксируется на этапе валидации, а не во время выполнения логики приложения.
Это снижает вероятность появления неконтролируемых состояний и облегчает сопровождение кода в долгосрочной перспективе.
При росте приложения shape становится центральным механизмом управления структурой данных. Он позволяет:
Каждая часть shape остаётся независимой, но участвует в общей модели объекта, формируя единый контракт данных.