OneOf и notOneOf

Валидационные схемы, построенные на основе Yup и используемые через YupResolver, позволяют задавать строгие правила допустимых значений для полей данных. Среди таких правил особое место занимают методы oneOf и notOneOf, которые обеспечивают контроль принадлежности значения к заранее определённому набору или его исключение из набора.

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


Семантика oneOf

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

Базовая сигнатура

yup.string().oneOf(arrayOfValues, message?)

Основные характеристики

  • Проверка выполняется по строгому равенству (===)
  • Поддерживаются строки, числа, булевы значения и смешанные наборы
  • Часто используется для реализации перечислений (enum)
  • Может применяться совместно с required

Простейшие примеры использования oneOf

Строковые значения

import * as yup from "yup";

const schema = yup.object({
  role: yup.string().oneOf(["admin", "user", "moderator"])
});

В данном случае любое значение, отличное от трёх перечисленных, приведёт к ошибке валидации.


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

const schema = yup.object({
  statusCode: yup.number().oneOf([200, 201, 204])
});

Используется для контроля допустимых кодов ответа или фиксированных числовых идентификаторов.


Булевы значения

const schema = yup.object({
  isActive: yup.boolean().oneOf([true])
});

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


Работа oneOf с YupResolver

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

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

const schema = yup.object({
  plan: yup.string().oneOf(["free", "pro", "enterprise"])
});

const form = useForm({
  resolver: yupResolver(schema)
});

При вводе значения вне списка ["free", "pro", "enterprise"] поле plan становится невалидным, а YupResolver возвращает структурированную ошибку.


Кастомизация сообщений ошибок в oneOf

const schema = yup.object({
  role: yup.string().oneOf(
    ["admin", "user"],
    "Недопустимая роль пользователя"
  )
});

Сообщение может быть как общим, так и специфичным для поля.


Семантика notOneOf

Метод notOneOf реализует противоположную логику: значение считается допустимым, если оно не входит в указанный список запрещённых значений.

Базовая сигнатура

yup.string().notOneOf(arrayOfValues, message?)

Примеры использования notOneOf

Запрет конкретных значений

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

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


Исключение числовых значений

const schema = yup.object({
  retryCount: yup.number().notOneOf([0])
});

Используется, когда определённые числовые значения считаются недопустимыми в бизнес-логике.


Поведение при пустых и неопределённых значениях

oneOf и notOneOf не заменяют базовую проверку наличия значения. Валидация null, undefined или пустой строки контролируется отдельно через required, nullable и default.

const schema = yup.object({
  role: yup.string().required().oneOf(["user", "admin"])
});

Без required поле может пройти валидацию как пустое значение, если оно не нарушает другие ограничения.


Сравнение oneOf и notOneOf

Характеристика oneOf notOneOf
Логика включение исключение
Поведение допускает только перечисленные значения запрещает перечисленные значения
Типичный сценарий enum-поля фильтрация запрещённых значений

Использование с массивами и сложными типами

Массив значений

const schema = yup.object({
  tags: yup.array().of(
    yup.string().oneOf(["news", "sports", "tech"])
  )
});

Каждый элемент массива проходит индивидуальную проверку.


Объекты и вложенные схемы

const schema = yup.object({
  settings: yup.object({
    theme: yup.string().oneOf(["dark", "light"]),
    language: yup.string().notOneOf(["klingon"])
  })
});

Динамические списки допустимых значений

oneOf и notOneOf поддерживают передачу значений, вычисляемых на этапе выполнения.

const allowedRoles = getRolesFromServer();

const schema = yup.object({
  role: yup.string().oneOf(allowedRoles)
});

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


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

Сравнение значений в oneOf и notOneOf выполняется без приведения типов. Это означает, что:

yup.string().oneOf([1, 2, 3])

строка "1" не будет считаться равной числу 1.

При необходимости нормализации данных используется transform:

yup.string()
  .transform((value) => Number(value))
  .oneOf([1, 2, 3]);

Поведение с mixed типом

const schema = yup.object({
  value: yup.mixed().oneOf([true, "enabled", 1])
});

mixed позволяет задавать набор допустимых значений разнородного типа, что полезно при интеграции с внешними API.


Комбинирование oneOf и notOneOf в одной схеме

const schema = yup.object({
  code: yup
    .string()
    .oneOf(["A", "B", "C"])
    .notOneOf(["C"])
});

В этом случае итоговый допустимый набор будет пересечением условий: "A" и "B".


Ошибки и приоритеты валидации

При конфликтующих условиях порядок применения правил влияет на итоговое сообщение об ошибке. Обычно oneOf и notOneOf проверяются после базовых ограничений типа (string, number), но до кастомных трансформаций.


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

При большом количестве значений (сотни и тысячи элементов) oneOf и notOneOf могут использовать структуру внутреннего поиска, эквивалентную проверке наличия в массиве. Для оптимизации часто используют предварительное преобразование в Set:

const allowed = new Set(fetchValues());

yup.string().test("oneOfSet", "", (value) => allowed.has(value));

Типичные сценарии применения

  • Реализация enum-полей в формах
  • Ограничение выбора тарифов, ролей, статусов
  • Защита от зарезервированных слов
  • Валидация кодов ответа и фиксированных идентификаторов
  • Контроль допустимых параметров API-запросов