Частичная валидация: pick и omit

Валидация сложных форм и структур данных редко требует проверки всего объекта целиком. На практике часто возникает необходимость валидировать только часть полей — например, при редактировании профиля пользователя, пошаговых формах или динамически изменяемых схемах. В таких сценариях в Yup ключевую роль играют методы pick и omit, позволяющие создавать производные схемы на основе существующих.


Базовая объектная схема и её структура

В основе работы с частичной валидацией лежит объектная схема:

import * as Yup from 'yup';

const userSchema = Yup.object({
  name: Yup.string().required(),
  email: Yup.string().email().required(),
  age: Yup.number().min(18),
  password: Yup.string().min(8),
  role: Yup.string().oneOf(['user', 'admin']),
});

Такая схема описывает полный набор полей. Однако не всегда требуется валидировать их одновременно. В некоторых случаях важно сузить контекст проверки.


Метод pick: выборочное включение полей

Метод pick позволяет создать новую схему, включающую только указанные поля исходной схемы. Это полезно при создании частичных форм или API-валидации отдельных частей объекта.

const loginSchema = userSchema.pick(['email', 'password']);

В результате получается схема, содержащая только email и password, со всеми их оригинальными правилами валидации.


Поведение pick при вложенных структурах

При использовании pick важно учитывать, что он работает только на верхнем уровне объекта. Вложенные схемы остаются неизменными.

const profileSchema = Yup.object({
  user: Yup.object({
    name: Yup.string(),
    email: Yup.string(),
  }),
  settings: Yup.object({
    theme: Yup.string(),
  }),
});

const userOnlySchema = profileSchema.pick(['user']);

Здесь сохраняется вся структура поля user, включая его внутренние правила.


Метод omit: исключение полей из схемы

Обратным инструментом является omit. Он создаёт новую схему, исключая перечисленные поля из исходной.

const publicUserSchema = userSchema.omit(['password']);

Такой подход часто используется при формировании публичных API-ответов или отображении данных без чувствительных полей.


Сравнение pick и omit

Оба метода решают схожую задачу, но подходят к ней с разных сторон:

  • pick — формирует «белый список» полей
  • omit — формирует «чёрный список» полей

Выбор между ними зависит от характера задачи. Если список полей фиксирован и небольшой, чаще применяется pick. Если необходимо исключить несколько полей из большой схемы, удобнее использовать omit.


Комбинирование pick и omit

В реальных проектах методы часто комбинируются для гибкого управления схемами:

const baseSchema = Yup.object({
  name: Yup.string(),
  email: Yup.string(),
  password: Yup.string(),
  role: Yup.string(),
  createdAt: Yup.date(),
});

const editableSchema = baseSchema
  .omit(['createdAt'])
  .pick(['name', 'email', 'role']);

В данном случае сначала исключается поле createdAt, затем выбирается подмножество оставшихся полей. Такой подход позволяет строить промежуточные уровни абстракции.


Частичная валидация в формах редактирования

В интерфейсах редактирования данных часто требуется проверять только изменяемые поля. Например, пользователь может обновлять профиль без изменения пароля.

const updateProfileSchema = userSchema.omit(['password']);

Это позволяет переиспользовать базовую схему без дублирования логики.


Динамическое формирование схем

В приложениях с гибкими интерфейсами схемы часто формируются на лету:

function createSchema(fields) {
  return userSchema.pick(fields);
}

const schema = createSchema(['email', 'role']);

Такой подход удобен при работе с конфигурационными формами, где набор полей зависит от ролей пользователя или состояния интерфейса.


Влияние на валидацию и ошибки

При использовании pick и omit Yup пересчитывает структуру схемы, но сохраняет исходные правила валидации для оставшихся полей. Ошибки будут возвращаться только для активных полей схемы.

Это особенно важно при работе с формами, где неактуальные поля не должны влиять на результат проверки.


Совместимость с вложенными объектами и массивами

Методы частичной валидации не рекурсивны. Это означает, что они не «проникают» внутрь массивов и вложенных объектов без явного указания.

const schema = Yup.object({
  user: Yup.object({
    name: Yup.string(),
    email: Yup.string(),
  }),
  tags: Yup.array().of(Yup.string()),
});

const partial = schema.pick(['user']);

В этом случае поле tags полностью исключается, но структура user остаётся нетронутой.


Использование в архитектуре форм

В архитектурах с разделением форм на шаги (wizard forms) частичная валидация становится ключевым инструментом. Каждый шаг может иметь собственную схему, полученную из базовой:

const step1Schema = userSchema.pick(['email']);
const step2Schema = userSchema.pick(['password']);
const step3Schema = userSchema.pick(['name', 'role']);

Такой подход позволяет централизованно управлять правилами валидации и избегать дублирования.


Типизация и TypeScript

При использовании Yup вместе с TypeScript частичная валидация влияет и на типы. Однако важно учитывать, что pick и omit не всегда автоматически корректно выводят строгие типы без дополнительной утилиты InferType.

import * as Yup from 'yup';

const schema = Yup.object({
  name: Yup.string().required(),
  age: Yup.number(),
});

type User = Yup.InferType<typeof schema>;

const partial = schema.pick(['name']);
type PartialUser = Yup.InferType<typeof partial>;

Тип PartialUser будет содержать только выбранные поля.


Ограничения частичной валидации

Несмотря на удобство, pick и omit имеют ряд ограничений:

  • отсутствие глубокого рекурсивного выбора
  • невозможность изменять правила внутри выбранных полей через эти методы
  • зависимость от исходной структуры схемы

В сложных сценариях может потребоваться комбинирование с when, lazy или ручное построение схем.


Производительность и переиспользование схем

Использование частичной валидации снижает необходимость дублирования схем, что положительно влияет на поддержку кода. Однако при частом создании новых схем через pick и omit в runtime может увеличиваться накладная стоимость создания объектов.

В большинстве приложений это не является критичным, но в высоконагруженных интерфейсах имеет смысл кэшировать производные схемы.


Практическая архитектура схем

В крупных проектах базовая схема обычно рассматривается как источник истины:

export const userSchema = Yup.object({
  name: Yup.string(),
  email: Yup.string(),
  password: Yup.string(),
  role: Yup.string(),
});

Далее производные схемы формируются через частичную выборку:

export const authSchema = userSchema.pick(['email', 'password']);

export const profileSchema = userSchema.omit(['password']);

Такой подход упрощает поддержку и синхронизацию бизнес-логики валидации.


Роль частичной валидации в масштабируемых приложениях

По мере роста приложения количество сценариев использования одной сущности увеличивается. Полная схема становится универсальным контрактом, а pick и omit — инструментами адаптации под конкретные контексты.

Это позволяет:

  • избегать дублирования правил
  • централизовать изменения валидации
  • снижать риск рассинхронизации логики между формами и API
  • строить предсказуемую структуру данных на уровне доменной модели