Массивы

Работа с массивами в схемах валидации строится вокруг типа array, который в Yup является одним из базовых контейнеров для описания коллекций данных. В связке с @hookform/resolvers/yup массивы приобретают особую значимость, поскольку именно через них чаще всего моделируются динамические формы: списки товаров, наборы тегов, участники, шаги, адреса и любые повторяющиеся структуры.

Конструкция массива в Yup задаётся через yup.array(). На этом уровне определяется сам факт того, что значение должно быть массивом, без уточнения структуры элементов.

import * as yup from "yup";

const schema = yup.object({
  tags: yup.array()
});

Такое описание гарантирует лишь одно: поле tags должно быть массивом. Однако элементы массива при этом остаются неограниченными, и любая строка, объект или число будет считаться допустимым значением внутри.

Для реальных сценариев этого недостаточно, поэтому следующим шагом становится определение типа элементов.

Типизация элементов массива через of

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

const schema = yup.object({
  tags: yup.array().of(yup.string())
});

Теперь каждый элемент массива обязан быть строкой. Любое несоответствие приведёт к ошибке валидации.

Аналогично можно задавать массивы чисел:

const schema = yup.object({
  scores: yup.array().of(yup.number())
});

При интеграции с YupResolver это особенно важно, так как ошибки формируются на уровне индексов массива и передаются в react-hook-form с точной привязкой к позиции элемента.

Ограничение длины массива

Массивы часто требуют контроля размера. Для этого используются методы min, max и length.

const schema = yup.object({
  tags: yup.array().of(yup.string()).min(1).max(5)
});

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

Метод length задаёт строго фиксированную длину:

const schema = yup.object({
  code: yup.array().of(yup.number()).length(4)
});

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

Обязательность массива

По умолчанию массив может отсутствовать или быть undefined. Для строгой формы используется required:

const schema = yup.object({
  tags: yup.array().of(yup.string()).required()
});

Если поле не передано вовсе, валидация провалится. Однако важно учитывать, что пустой массив ([]) при этом считается валидным значением. Это частая особенность, влияющая на бизнес-логику форм.

Для запрета пустых массивов применяется комбинация required и min(1):

tags: yup.array().of(yup.string()).required().min(1)

Массивы объектов

На практике наиболее распространённый сценарий — массив объектов. Это основа динамических списков форм.

const schema = yup.object({
  users: yup.array().of(
    yup.object({
      name: yup.string().required(),
      age: yup.number().min(18).required()
    })
  )
});

Каждый элемент массива представляет собой объект с собственной схемой. YupResolver в этом случае формирует вложенные ошибки вида:

  • users[0].name
  • users[1].age

Такая структура напрямую соответствует механике react-hook-form, что обеспечивает точную привязку ошибок к полям интерфейса.

Вложенные массивы

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

const schema = yup.object({
  matrix: yup.array().of(
    yup.array().of(yup.number())
  )
});

Здесь каждый элемент верхнего массива сам является массивом чисел. Ошибки валидации будут содержать двухуровневую индексацию:

  • matrix[0][2]

Такая структура требует аккуратного управления в UI, так как любая ошибка должна быть сопоставлена с конкретной ячейкой.

Проверка содержимого массива через test

Иногда стандартных методов недостаточно, и требуется кастомная логика. Для этого используется test.

const schema = yup.object({
  ids: yup.array().of(yup.number()).test(
    "unique",
    "Элементы массива должны быть уникальными",
    (value) => {
      if (!value) return true;
      return new Set(value).size === value.length;
    }
  )
});

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

Работа с nullable и default

Массивы часто взаимодействуют с состояниями отсутствия данных. nullable позволяет явно разрешить null:

tags: yup.array().of(yup.string()).nullable()

В таком случае допустимы три состояния:

  • массив
  • null
  • отсутствие значения (в зависимости от required)

Метод default позволяет задавать начальное значение:

tags: yup.array().of(yup.string()).default([])

Это особенно важно в связке с YupResolver, поскольку React Hook Form ожидает определённую структуру данных при инициализации формы. Отсутствие массива может приводить к ошибкам при рендере динамических списков.

Массивы и преобразование данных

Метод transform используется для нормализации входных данных перед валидацией.

tags: yup.array().of(yup.string()).transform((value) => {
  if (typeof value === "string") {
    return value.split(",");
  }
  return value;
})

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

Смешанные массивы и mixed

Yup позволяет создавать массивы с неоднородными типами через mixed, хотя это требует осторожности.

const schema = yup.object({
  values: yup.array().of(yup.mixed())
});

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

Более контролируемый подход — использование oneOf или кастомных тестов внутри mixed.

Интеграция массивов с YupResolver

При подключении схемы к react-hook-form через yupResolver массивы начинают играть ключевую роль в синхронизации состояния формы и валидации.

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

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

При изменении массива React Hook Form передаёт обновлённое значение в Yup, который выполняет валидацию всей структуры целиком или частично, в зависимости от режима.

Ошибки возвращаются в виде вложенного объекта:

{
  users: [
    { name: "Ошибка" },
    { age: "Слишком мало" }
  ]
}

Это позволяет точно отображать ошибки в динамических списках.

Динамические массивы и управление полями

Хотя Yup отвечает за валидацию, управление длиной массива обычно реализуется через useFieldArray из React Hook Form. Однако схема Yup должна быть заранее готова к динамическому изменению структуры.

users: yup.array().of(
  yup.object({
    name: yup.string().required()
  })
)

При добавлении или удалении элементов схема не меняется, что обеспечивает стабильную валидацию независимо от размера массива.

Индексация ошибок и её особенности

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

  • первый элемент: [0]
  • второй элемент: [1]

При вложенных структурах индексация расширяется:

  • items[0].subItems[2].value

Такая система требует согласованности между UI и схемой, иначе отображение ошибок становится некорректным.

Частичные массивы и sparse значения

Массивы могут содержать «дыры» — пропущенные индексы. Yup по умолчанию игнорирует такие особенности JavaScript, но при необходимости можно учитывать их через кастомные проверки.

yup.array().of(yup.string().required())

Если элемент отсутствует, он будет интерпретирован как undefined, что вызовет ошибку при наличии required.

Комбинирование условий в массивах

Массивы часто требуют комплексной валидации, где одновременно применяются ограничения на элементы и саму коллекцию:

yup.array()
  .of(yup.number().min(0).max(100))
  .min(3)
  .max(10)
  .required()

Здесь контролируется:

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

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

Массивы как основа сложных форм

В большинстве реальных интерфейсов массивы выступают фундаментом структурирования данных. Списки товаров, адресов, телефонов, комментариев и вложенных сущностей строятся именно через yup.array().of(...).

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