Проверка на соответствие значению

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

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

oneOf — проверка принадлежности множеству

Метод oneOf задаёт список допустимых значений, которым должно соответствовать поле.

import * as Yup fr om "yup";

const schema = Yup.object({
  role: Yup.string().oneOf(["admin", "editor", "viewer"])
});

В этом случае значение role должно строго совпадать с одним из элементов массива. Любое отклонение приводит к ошибке валидации.

Особенности поведения:

  • сравнение происходит через строгое равенство (===)
  • порядок значений не влияет на результат
  • тип значения должен совпадать

notOneOf — исключение значений

Обратный механизм позволяет запретить определённые значения:

const schema = Yup.object({
  username: Yup.string().notOneOf(["admin", "root", "system"])
});

Такая проверка применяется для исключения зарезервированных слов, запрещённых статусов или конфликтующих идентификаторов.

Проверка равенства значений полей

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

ref и динамическое сравнение

const schema = Yup.object({
  password: Yup.string().required(),
  confirmPassword: Yup.string()
    .oneOf([Yup.ref("password")], "Пароли должны совпадать")
});

Здесь значение confirmPassword динамически сравнивается с password.

Ключевые особенности:

  • Yup.ref создаёт ссылку на другое поле
  • сравнение происходит в момент валидации
  • изменение исходного поля автоматически влияет на результат

Сравнение числовых значений

const schema = Yup.object({
  min: Yup.number(),
  max: Yup.number()
    .min(Yup.ref("min"), "max должен быть больше или равен min")
});

Такой подход часто применяется в диапазонах значений: цены, даты, лимиты.

Глубокое и поверхностное сравнение

Стандартные методы oneOf и notOneOf используют сравнение по ссылке или примитивное равенство. Это создаёт важные ограничения при работе с объектами.

Ограничения объектов

const schema = Yup.object({
  config: Yup.object().oneOf([{ theme: "dark" }])
});

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

В таких случаях требуется использовать пользовательскую проверку.

Пользовательская логика через test

Метод test предоставляет полный контроль над сравнением значений.

const schema = Yup.object({
  status: Yup.string().test(
    "is-valid-status",
    "Недопустимый статус",
    value => value === "active" || value === "pending"
  )
});

Сравнение может быть расширено до любой логики:

const schema = Yup.object({
  price: Yup.number().test(
    "compare-to-lim it",
    "Цена превышает лимит",
    function (value) {
      const limit = this.parent.limit;
      return value <= limit;
    }
  )
});

Здесь используется контекст this.parent для доступа к другим полям объекта.

Условное сравнение значений

Механизм when позволяет изменять правила проверки в зависимости от значения другого поля.

const schema = Yup.object({
  type: Yup.string(),
  value: Yup.string().when("type", {
    is: "email",
    then: schema => schema.email(),
    otherwise: schema => schema.min(3)
  })
});

Сравнение значения используется как триггер для выбора логики валидации.

Расширенный вариант:

const schema = Yup.object({
  role: Yup.string().when("department", (department, schema) => {
    if (department === "security") {
      return schema.oneOf(["admin"]);
    }
    return schema.oneOf(["user", "viewer"]);
  })
});

Типовые сценарии сравнения значений

Подтверждение пароля

Наиболее частый кейс использования равенства полей.

const schema = Yup.object({
  password: Yup.string().required(),
  confirmPassword: Yup.string()
    .oneOf([Yup.ref("password")], "Пароли не совпадают")
    .required()
});

Проверка перечислений

const schema = Yup.object({
  status: Yup.string().oneOf(["draft", "published", "archived"])
});

Такая схема фиксирует допустимые состояния сущности.

Ограничение бизнес-логики

const schema = Yup.object({
  action: Yup.string().test(
    "valid-transition",
    "Недопустимый переход статуса",
    function (value) {
      const current = this.parent.currentStatus;

      const transitions = {
        draft: ["published"],
        published: ["archived"],
        archived: []
      };

      return transitions[current]?.includes(value);
    }
  )
});

Особенности сравнения значений

NaN и числовые сравнения

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

Yup.number().test(
  "is-not-nan",
  "Некорректное число",
  value => typeof value === "number" && !Number.isNaN(value)
);

undefined и null

  • undefined обычно трактуется как отсутствие значения
  • null может считаться валидным значением при явном разрешении
Yup.string().nullable().oneOf([null, "active"]);

Приведение типов

Yup может выполнять преобразование типов перед сравнением, особенно при использовании .transform():

Yup.number()
  .transform(value => Number(value))
  .oneOf([10, 20, 30]);

Сравнение значений в массивах

При работе с массивами сравнение применяется к каждому элементу:

const schema = Yup.array().of(
  Yup.string().oneOf(["red", "green", "blue"])
);

Дополнительно можно проверять совпадение массива целиком через кастомные тесты:

Yup.array().test(
  "match-exact",
  "Массив не совпадает",
  value => JSON.stringify(value) === JSON.stringify(["a", "b"])
);

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

Сравнение с использованием контекста схемы

Валидация может зависеть от нескольких уровней данных:

const schema = Yup.object({
  minAge: Yup.number(),
  age: Yup.number().test(
    "greater-than-min",
    "Возраст меньше допустимого",
    function (value) {
      return value >= this.parent.minAge;
    }
  )
});

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