Валидация в Superstruct строится вокруг декларативного описания структур данных и последовательного применения проверок к входящим значениям. Библиотека рассматривает данные не как абстрактные объекты, а как набор ограничений, которые должны быть выполнены в момент исполнения кода.
Основой подхода является описание структуры через функции-конструкторы, называемые структурными примитивами. Каждая такая функция описывает ожидаемый тип значения и набор правил его допустимости.
import { string, number, object } from 'superstruct'
const User = object({
name: string(),
age: number()
})
В этом примере создаётся структура User, которая
определяет форму объекта. Важно, что само описание не выполняет проверку
— оно лишь задаёт правила, которые будут применены позже.
Ключевой принцип заключается в разделении этапа описания схемы и
этапа валидации данных. Структура выступает как спецификация, а проверка
выполняется отдельно через функции validate или
assert.
import { validate } from 'superstruct'
const [error, result] = validate({ name: 'Alex', age: 30 }, User)
Такой подход позволяет переиспользовать одну и ту же структуру в разных частях приложения без дублирования логики.
Процесс проверки данных в Superstruct проходит несколько логических этапов.
1. Коэрция (преобразование) На этом этапе значение может быть приведено к ожидаемому виду. Например, строка может быть преобразована в число, если используется соответствующий коэрс-структ.
import { coerce, number, string } from 'superstruct'
const NumberFromString = coerce(number(), string(), (value) => Number(value))
2. Базовая проверка типа После возможного преобразования выполняется проверка соответствия базовому типу: строка, число, массив, объект и т.д.
3. Проверка ограничений На этом этапе применяются дополнительные условия: длина строки, диапазоны значений, регулярные выражения, пользовательские предикаты.
import { size, string } from 'superstruct'
const Username = size(string(), 3, 20)
4. Функциональная валидация (refinement) Финальный этап позволяет наложить произвольную бизнес-логику через функцию проверки.
import { refine, string } from 'superstruct'
const EvenLengthString = refine(string(), 'EvenLengthString', (value) => {
return value.length % 2 === 0
})
При провале проверки Superstruct формирует структурированную ошибку, содержащую информацию о пути до проблемного значения и причине несоответствия.
Типичная ошибка включает:
path)import { assert } from 'superstruct'
assert({ name: 123 }, User)
В случае ошибки выбрасывается исключение, содержащее подробное дерево причин. Это позволяет точно локализовать проблему даже в глубоко вложенных структурах.
Одним из ключевых механизмов является возможность композиции примитивов в сложные схемы. Структуры могут быть вложенными, комбинированными и переиспользуемыми.
const Address = object({
city: string(),
zip: string()
})
const User = object({
name: string(),
address: Address
})
Такой подход обеспечивает модульность и снижает дублирование описаний.
Для описания альтернативных или комбинированных форм данных используются специальные структуры.
Union (объединение типов)
import { union } from 'superstruct'
const ID = union([string(), number()])
Intersection (пересечение типов)
import { intersection } from 'superstruct'
const A = object({ a: string() })
const B = object({ b: number() })
const AB = intersection([A, B])
Union определяет допустимость одного из вариантов, тогда как intersection требует соблюдения всех условий одновременно.
Superstruct позволяет создавать полностью кастомные валидаторы, расширяя базовую систему типов.
import { define } from 'superstruct'
const PositiveNumber = define('PositiveNumber', (value) => {
return typeof value === 'number' && value > 0
})
Такие структуры становятся полноценными элементами системы и могут участвовать в композиции наравне с встроенными типами.
Одной из особенностей Superstruct является автоматический вывод TypeScript-типов из описанных структур. Это позволяет синхронизировать runtime-валидацию и compile-time типизацию.
import { Infer, object, string, number } from 'superstruct'
const User = object({
name: string(),
age: number()
})
type UserType = Infer<typeof User>
В результате тип UserType автоматически соответствует
структуре данных, исключая необходимость ручного дублирования типов.
В зависимости от используемого метода проверки, библиотека может вести себя по-разному:
assert — выбрасывает исключениеvalidate — возвращает кортеж
[error, result]is — возвращает booleanimport { is } from 'superstruct'
is('hello', string()) // true
is(123, string()) // false
Такое разделение позволяет выбирать модель обработки ошибок в зависимости от архитектуры приложения.
Особое значение имеет механизм отслеживания пути до некорректного значения. При вложенных объектах путь формируется как последовательность ключей.
{
user: {
profile: {
age: 'not-a-number'
}
}
}
Ошибка будет содержать путь вида user.profile.age, что
существенно упрощает диагностику.
Валидация в Superstruct выполняется лениво: проверка происходит только при явном вызове функции валидации. Это позволяет строить сложные схемы без затрат до момента фактического использования.
Каждая структура является функцией, возвращающей новое описание, что делает систему функционально-композиционной и предсказуемой.
Вся система валидации строится на трёх базовых принципах:
Такая модель делает Superstruct предсказуемым инструментом для проверки данных на этапе выполнения, сохраняя при этом строгую связь с типами на этапе компиляции TypeScript.