Типы ошибок

Валидация данных в Superstruct построена вокруг строгого описания структур и детализированной информации о том, почему проверка не прошла. Основным элементом системы является специализированный объект ошибки, который содержит не только факт неудачи, но и полный контекст: где именно произошло нарушение, какое значение ожидалось и что было получено фактически.


StructError как основной тип ошибок

При нарушении схемы данных Superstruct генерирует экземпляр StructError. Это не просто сообщение об ошибке, а структурированный объект, предназначенный для машинной обработки.

Ключевые свойства StructError:

  • name — фиксированное значение "StructError", позволяющее однозначно идентифицировать тип ошибки
  • message — человекочитаемое описание первой обнаруженной проблемы
  • value — исходное значение, которое проходило валидацию
  • failures — функция-генератор, возвращающая полный список всех найденных нарушений
  • error — дополнительное поле для вложенных ошибок (используется в сложных структурах)

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


Объекты failures и детальная диагностика

Каждая ошибка внутри структуры описывается через объект failure. Он содержит точную информацию о месте и характере нарушения.

Основные поля failure:

  • path — путь к значению в структуре данных (массив ключей)
  • value — фактическое значение, вызвавшее ошибку
  • type — ожидаемый тип или правило
  • refinement — имя кастомной проверки, если ошибка возникла в refine
  • message — текстовое описание нарушения

Пример логики формирования пути:

{
  user: {
    profile: {
      age: "not a number"
    }
  }
}

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

["user", "profile", "age"]

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


Path и branch как механизм трассировки

Superstruct различает два важных понятия: path и branch.

  • path — абсолютный путь от корня структуры до проблемного значения
  • branch — поддерево данных, относящееся к текущему узлу проверки

branch особенно полезен при рекурсивной валидации, когда структура содержит вложенные объекты или массивы. Он позволяет восстановить контекст без обращения к исходному объекту целиком.


Ошибки типов и несоответствие структур

Наиболее частый класс ошибок связан с несовпадением ожидаемого и фактического типа данных.

Типичные случаи:

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

В таких ситуациях type в failure указывает на ожидаемое ограничение, например:

expected: "number"
received: "string"

Это делает диагностику предсказуемой и унифицированной.


Ошибки union-структур

Union-структуры (union([...])) проверяют значение по нескольким альтернативным схемам. Ошибка возникает только в том случае, если ни одна из веток не подошла.

Особенность таких ошибок заключается в агрегации:

  • каждая ветка генерирует собственный набор failures
  • итоговая ошибка содержит объединённый список всех попыток проверки

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


Ошибки refinement и пользовательские проверки

Refinement-ошибки возникают при использовании кастомных предикатов.

Пример логики:

  • базовая структура проходит проверку
  • затем выполняется дополнительное условие
  • при его нарушении создаётся failure с полем refinement

В отличие от типовых ошибок, здесь важен не тип данных, а логическое условие, например:

  • число должно быть положительным
  • строка должна соответствовать формату
  • объект должен удовлетворять бизнес-правилу

Агрегация ошибок и поведение StructError

StructError может содержать несколько независимых нарушений. Это достигается за счёт ленивой генерации через failures().

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

  • ошибки не всегда выбрасываются сразу
  • сбор нарушений происходит при обходе структуры
  • итоговый объект может содержать десятки failure-элементов

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


Формирование сообщений и приоритет ошибок

Поле message в StructError обычно отражает первую найденную проблему. Однако оно не является исчерпывающим источником информации.

Приоритет формирования:

  1. первая ошибка в порядке обхода структуры
  2. наиболее глубокий узел при рекурсивной проверке
  3. нарушение, возникшее раньше в цепочке валидации

Полная диагностика всегда строится через failures(), а не через message.


Обработка вложенных ошибок

При сложных структурах возможна ситуация, когда одна ошибка содержит вложенные StructError.

Это происходит при:

  • композиции структур
  • использовании nested объектов
  • проверке массивов структурированных элементов

В таких случаях ошибка превращается в дерево, где каждый узел содержит собственные failures и контекст.


Особенности сериализации ошибок

StructError содержит данные, которые не всегда напрямую сериализуются в JSON:

  • функции (failures)
  • циклические ссылки в branch
  • внутренние метаданные проверки

Поэтому для передачи ошибок обычно используют преобразование:

  • извлечение failures
  • построение плоского списка
  • нормализация path и message

Практика анализа ошибок

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

  • path указывает место дефекта
  • value показывает фактическое состояние
  • type или refinement определяет правило нарушения

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