Валидация данных в Superstruct строится вокруг предсказуемого формирования ошибок и их детализированного разбора. В отличие от подходов, где ошибка представляет собой строку или единичное исключение, здесь результат валидации содержит структурированное дерево причин, позволяющее точно локализовать несоответствие.
Ключевая особенность заключается в том, что ошибка не является конечным объектом — она выступает контейнером для последовательности «сбоев», каждый из которых описывает конкретный участок структуры данных.
При использовании функции validate(value, struct)
результатом становится кортеж:
error — объект ошибки или undefinedresult — преобразованные данные (если валидация
успешна)Если ошибка присутствует, она содержит метод failures(),
который возвращает итерируемую последовательность диагностических
записей.
Каждая запись описывает отдельное нарушение:
Пример логики извлечения:
const [error, data] = validate(input, User);
if (error) {
for (const failure of error.failures()) {
console.log(failure.path, failure.message, failure.value);
}
}
Одним из ключевых элементов диагностического режима является построение пути до ошибки.
Путь представляет собой массив сегментов, отражающих навигацию по вложенным структурам:
Например:
['user', 'addresses', 0, 'zip']
Такой путь позволяет точно определить место нарушения без необходимости ручного обхода данных.
Особенность реализации заключается в том, что путь формируется лениво — на этапе обхода структуры, а не постфактум.
Superstruct поддерживает агрегацию ошибок на всех уровнях
вложенности. Это означает, что при валидации сложного объекта не
происходит «раннего выхода» на первой ошибке (если не используется
assert).
Рассмотрим поведение:
validate собираются все ошибкиassert выбрасывается первая
обнаруженная ошибкаis ошибки полностью игнорируютсяТакой дизайн позволяет переключать режим диагностики без изменения структуры схемы.
Функция assert(value, struct) переводит результат в
режим исключений.
В этом случае ошибка содержит расширенную диагностическую информацию, но выбрасывается как исключение, что удобно для сценариев строгой проверки входных данных.
Структура ошибки включает:
При отладке важно учитывать, что assert не предназначен
для накопления ошибок, а только для немедленного прерывания
исполнения.
Каждый примитивный тип и комбинированная структура могут содержать пользовательские сообщения.
Пример определения:
import { string } from 'superstruct';
const Name = string({
message: 'Имя должно быть строкой'
});
В диагностическом режиме такие сообщения подставляются в failure вместо стандартных описаний типов.
Это влияет на:
Функция refine добавляет пользовательские условия
проверки поверх базовой структуры.
import { refine, string } from 'superstruct';
const NonEmpty = refine(string(), 'NonEmpty', (value) => {
return value.length > 0;
});
При включении debug-режима refine добавляет дополнительный уровень контекста в failure:
Таким образом, ошибки становятся не только синтаксическими, но и семантическими.
Механизмы преобразования данных (coerce,
mask) влияют на диагностический вывод.
coerce изменяет входные данные до проверкиmask принудительно приводит структуру к типуВ debug-режиме важно различать:
Failure-объекты могут содержать оба представления, что позволяет анализировать цепочку трансформаций.
При работе с коллекциями ошибок поведение отличается в зависимости от структуры:
Пример диагностического результата:
users[2].email → invalid format
users[4].age → expected number, received string
Такой формат позволяет использовать ошибки напрямую в логировании без дополнительной обработки.
Superstruct позволяет сохранять контекст выполнения в процессе валидации.
Контекст может включать:
Это особенно важно при построении сложных схем, где одна и та же структура используется в разных частях системы.
При глубокой вложенности debug-режим сохраняет стек прохождения структуры.
Каждый уровень добавляет новый сегмент пути, формируя полную трассировку:
Это позволяет точно определить точку сбоя даже в схемах с десятками уровней вложенности.
Failures не вычисляются заранее. Они создаются только при итерации
через failures().
Это означает:
Такой подход снижает накладные расходы при работе с большими структурами данных.
Структурированные ошибки легко интегрируются в системы логирования благодаря предсказуемому формату:
Это позволяет:
При использовании объединений (union) debug-режим
фиксирует все неудачные попытки сопоставления.
Каждая альтернатива сохраняет собственный набор failures, что позволяет определить:
Это особенно полезно при сложных схемах с несколькими допустимыми форматами данных.