Сортировка и ensure

В библиотеке валидации Yup метод ensure() используется для нормализации входных данных, приводя undefined и null к безопасным значениям, соответствующим типу схемы. Его основная цель — устранение необходимости вручную проверять наличие значения перед дальнейшей обработкой.

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

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

Строки

Yup.string().ensure()
  • undefined'' (пустая строка)
  • null''
  • уже существующая строка остаётся без изменений

Числа

Yup.number().ensure()
  • undefined0
  • null0
  • NaN0
  • корректные числа не изменяются

Массивы

Yup.array().ensure()
  • undefined[]
  • null[]
  • массив сохраняется без изменений

Таким образом, ensure() выступает как механизм дефолтной нормализации, устраняя “пустые” значения на уровне схемы.


Нормализация данных как этап валидационного конвейера

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

Внутренне схема проходит несколько этапов:

  1. Приведение значения к базовому типу
  2. Применение ensure() (если задан)
  3. Выполнение transform()
  4. Проверка test() и валидаторов
  5. Применение default() (в зависимости от цепочки)

Такое поведение важно учитывать при построении сложных схем, особенно при работе с массивами и объектами.


Сортировка массивов в схемах Yup

Сортировка не является встроенной функцией библиотеки, однако реализуется через механизм transform() внутри массивов.

Yup.array().transform((value, originalValue) => {
  if (!Array.isArray(value)) return value;

  return [...value].sort((a, b) => a - b);
});

Базовая сортировка примитивов

Для числовых массивов:

const schema = Yup.array()
  .of(Yup.number())
  .transform((value) => {
    if (!Array.isArray(value)) return value;
    return value.slice().sort((a, b) => a - b);
  });

Для строковых массивов:

const schema = Yup.array()
  .of(Yup.string())
  .transform((value) => {
    if (!Array.isArray(value)) return value;
    return value.slice().sort((a, b) => a.localeCompare(b));
  });

Сортировка объектов внутри массива

При работе с массивами объектов сортировка требует явного указания ключа:

const schema = Yup.array()
  .of(
    Yup.object({
      name: Yup.string(),
      age: Yup.number(),
    })
  )
  .transform((value) => {
    if (!Array.isArray(value)) return value;

    return value.slice().sort((a, b) => a.age - b.age);
  });

Сортировка может выполняться по нескольким полям:

return value.slice().sort((a, b) => {
  if (a.age === b.age) {
    return a.name.localeCompare(b.name);
  }
  return a.age - b.age;
});

Использование ensure вместе с сортировкой

Комбинация ensure() и сортировки особенно полезна при обработке входных данных из форм или API, где возможны null и отсутствующие значения.

const schema = Yup.array()
  .ensure()
  .of(Yup.number())
  .transform((value) => {
    return value.slice().sort((a, b) => a - b);
  });

В этом случае:

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

Влияние порядка ensure и transform

Важно учитывать, что ensure() выполняется до transform().

Это означает:

  • сначала данные приводятся к безопасному типу
  • затем применяется сортировка или другая модификация

Пример цепочки:

Yup.array()
  .ensure()
  .transform((value) => value.sort());

Если же transform возвращает null или другой тип, ensure() уже не повторяется, поэтому порядок влияет на итоговую структуру.


Сортировка и валидация: взаимодействие

Сортировка внутри transform() не влияет на валидаторы test() в прямом смысле, но может менять результат проверки.

Пример:

Yup.array()
  .of(Yup.number().min(10))
  .transform((value) => value.sort((a, b) => a - b));

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


Практические сценарии использования

Нормализация данных формы

const schema = Yup.object({
  tags: Yup.array()
    .ensure()
    .of(Yup.string())
    .transform((value) => value.sort()),
});

Вход:

{ tags: undefined }

Результат:

{ tags: [] }

Подготовка данных API

const schema = Yup.array()
  .ensure()
  .of(
    Yup.object({
      id: Yup.number(),
      priority: Yup.number(),
    })
  )
  .transform((value) =>
    value.sort((a, b) => b.priority - a.priority)
  );

Особенности работы ensure в цепочке схем

ensure() не является валидатором в классическом смысле, а относится к механизмам нормализации. Его ключевые особенности:

  • не генерирует ошибки валидации
  • изменяет входные данные до проверки
  • не зависит от required()
  • может использоваться совместно с default(), но не заменяет его

Ограничения и поведение при сложных схемах

При комбинировании с трансформациями следует учитывать:

  • ensure() не предотвращает появление NaN в сложных вычислениях
  • сортировка внутри transform() всегда должна учитывать неизменяемость массива
  • повторное применение transform() может приводить к перезаписи результата предыдущих трансформаций

Пример безопасного подхода:

Yup.array()
  .ensure()
  .of(Yup.number())
  .transform((value) => {
    if (!Array.isArray(value)) return value;
    return [...value].sort((a, b) => a - b);
  });

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

Для вложенных массивов ensure применяется только на уровне текущей схемы:

Yup.object({
  matrix: Yup.array().of(
    Yup.array().ensure().of(Yup.number())
  ),
});

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