Смешанные типы

Смешанный тип в системе схем Yup представляет собой универсальный контейнер для значений, которые не вписываются в строгую типизацию строк, чисел, массивов или объектов. Он используется в ситуациях, когда форма данных может изменяться в процессе работы приложения или когда структура заранее не определена. В экосистеме Yup именно mixed() становится базовой точкой расширения для построения гибких и условных валидаторов, а при использовании вместе с YupResolver он позволяет интегрировать динамические схемы в формы React Hook Form без потери контроля над проверками.

Внутренне mixed в Yup — это абстрактный тип, который не накладывает ограничений на структуру значения до тех пор, пока они явно не определены разработчиком. Это означает, что любое значение проходит первичную стадию принятия, а затем подвергается цепочке пользовательских правил.

Ключевая особенность заключается в том, что mixed:

  • не выполняет строгую типовую проверку по умолчанию
  • поддерживает кастомные правила через .test()
  • позволяет трансформации через .transform()
  • может участвовать в условной логике .when()
  • поддерживает отложенное определение через lazy()

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

Базовая структура yup.mixed()

Создание схемы начинается с объявления универсального типа:

import * as yup from "yup";

const schema = yup.mixed();

На этом уровне схема допускает любое значение: строку, число, объект, null, undefined, массив или даже экземпляры классов. Однако реальная сила проявляется при добавлении ограничений.

Ограничение допустимых типов

Несмотря на универсальность, mixed позволяет фиксировать допустимые типы данных через явные проверки:

const schema = yup.mixed().oneOf([true, false]);

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

Другой вариант — проверка типа вручную:

const schema = yup.mixed().test(
  "is-string-or-number",
  "Недопустимый тип",
  (value) => typeof value === "string" || typeof value === "number"
);

Трансформация значений

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

const schema = yup.mixed().transform((value) => {
  if (value === "") return null;
  return value;
});

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

Работа с неопределёнными структурами

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

const schema = yup.object({
  config: yup.mixed().test(
    "is-valid-config",
    "Некорректная конфигурация",
    (value) => typeof value === "object" && value !== null
  )
});

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

Условная логика через when()

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

const schema = yup.object({
  type: yup.string(),
  payload: yup.mixed().when("type", {
    is: "file",
    then: (schema) =>
      schema.test("is-file", "Ожидается файл", (value) => value instanceof File),
    otherwise: (schema) =>
      schema.test("is-string", "Ожидается строка", (value) => typeof value === "string")
  })
});

В такой конфигурации mixed выступает как переключатель логики, делегируя проверку контексту.

Lazy-схемы и динамическая типизация

Функция lazy() расширяет возможности mixed, позволяя строить схему на основе значения в момент валидации:

const schema = yup.lazy((value) => {
  if (Array.isArray(value)) {
    return yup.array();
  }
  return yup.object();
});

Подобная конструкция часто используется для обработки структур с переменной вложенностью.

Особенности интеграции с YupResolver

При использовании YupResolver в связке с React Hook Form смешанные типы требуют особого внимания, поскольку типизация формы может становиться менее строгой.

import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";

const { register, handleSubmit } = useForm({
  resolver: yupResolver(schema)
});

В момент разрешения схемы mixed-поля возвращаются без жёсткой типовой гарантии, что требует дополнительной обработки на уровне приложения. Особенно это проявляется в TypeScript, где результат валидации может быть расширен до unknown.

Обработка файлов и бинарных данных

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

const schema = yup.object({
  avatar: yup.mixed().test(
    "fileSize",
    "Файл слишком большой",
    (value) => value && value.size <= 2000000
  )
});

Здесь mixed становится контейнером для File или Blob, а логика проверки полностью определяется вручную.

Полиморфные структуры данных

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

const schema = yup.mixed().test(
  "is-valid-shape",
  "Неверная структура",
  (value) =>
    (value && typeof value === "string") ||
    (value && typeof value === "object" && "id" in value)
);

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

Взаимодействие с типизацией TypeScript

В TypeScript mixed часто приводит к ослаблению строгой типизации, так как итоговый тип становится неопределённым. Для компенсации этого эффекта используются явные приведения и уточняющие схемы.

const schema: yup.MixedSchema<string | number> = yup.mixed();

Однако при использовании YupResolver итоговый тип результата часто требует дополнительного сужения через пользовательские функции проверки.

Вложенные смешанные структуры

Допускается использование mixed внутри сложных объектов, где отдельные поля имеют динамическую природу.

const schema = yup.object({
  metadata: yup.mixed(),
  data: yup.object({
    payload: yup.mixed(),
    timestamp: yup.number()
  })
});

Такая структура характерна для систем логирования, телеметрии и универсальных API-обёрток.

Ограничения и особенности поведения

При всей гибкости mixed имеет ряд особенностей:

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

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