Object

Работа с объектами в Superstruct строится вокруг декларативного описания схемы данных, где каждый ключ объекта явно связан с типом и правилами валидации. Объектная структура используется для описания JSON-подобных данных, конфигураций, DTO и любых вложенных сущностей, требующих строгой проверки формы.


Базовое создание объектной структуры

Основой является функция object, принимающая описание полей:

import { object, string, number } from 'superstruct'

const User = object({
  name: string(),
  age: number(),
})

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


Принцип строгой структуры

По умолчанию объект в Superstruct является строгим по полям. Это означает, что наличие лишних ключей может считаться ошибкой в зависимости от режима использования.

const User = object({
  name: string(),
})

validate({ name: 'Alex', extra: true }, User)

Лишние поля не участвуют в проверке структуры и могут быть отброшены или вызовут ошибку в зависимости от конфигурации обработки результата.


Обязательные и опциональные поля

Все поля внутри object считаются обязательными, если не указано иное.

Использование optional

import { object, string, optional } from 'superstruct'

const User = object({
  name: string(),
  nickname: optional(string()),
})

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


Значения по умолчанию

Для задания дефолтных значений используется defaulted. Это позволяет автоматически заполнять отсутствующие поля.

import { object, string, defaulted } from 'superstruct'

const User = object({
  name: string(),
  role: defaulted(string(), 'user'),
})

Если поле role отсутствует, оно будет автоматически заполнено значением 'user'.


Вложенные объекты

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

import { object, string, number } from 'superstruct'

const Address = object({
  city: string(),
  zip: number(),
})

const User = object({
  name: string(),
  address: Address,
})

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


Композиция объектов

Объекты можно комбинировать через переиспользование схем:

const BaseUser = object({
  name: string(),
})

const AdminUser = object({
  ...BaseUser.schema,
  permissions: string(),
})

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


Частичные структуры

Для создания объектов, где все поля становятся необязательными, используется концепция частичной структуры через обёртки или ручное применение optional.

const PartialUser = object({
  name: optional(string()),
  age: optional(number()),
})

Это особенно полезно для PATCH-операций и частичных обновлений данных.


Валидация вложенных значений

Вложенные структуры проверяются глубоко. Ошибка может возникнуть на любом уровне:

const User = object({
  profile: object({
    email: string(),
  }),
})

Если profile.email не соответствует типу string, ошибка будет привязана к конкретному пути вложенности.


Обработка ошибок структуры

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

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

  • путь: profile.email
  • причина: ожидалась строка, получено число

Это позволяет точно локализовать источник некорректных данных без дополнительного анализа входного объекта.


Использование с массивами объектов

Object-структуры часто комбинируются с массивами:

import { array, object, string } from 'superstruct'

const User = object({
  name: string(),
})

const Users = array(User)

Каждый элемент массива проходит отдельную проверку по схеме User.


Интеграция с coerce

Объекты могут автоматически преобразовывать входные данные перед проверкой:

import { object, coerce, string, number } from 'superstruct'

const User = coerce(
  object({
    age: number(),
  }),
  object({
    age: string(),
  }),
  (value) => ({
    age: Number(value.age),
  })
)

Это позволяет работать с “грязными” входными данными, например строковыми значениями чисел.


Использование refine для объектных структур

Для добавления пользовательских ограничений используется refine:

import { object, string, refine } from 'superstruct'

const User = refine(
  object({
    password: string(),
    confirm: string(),
  }),
  (value) => value.password === value.confirm
)

Если условие не выполняется, структура считается невалидной.


Работа с динамическими ключами через Record

Когда структура объекта не фиксирована, используется record:

import { record, string, number } from 'superstruct'

const Scores = record(string(), number())

Такой объект позволяет произвольные строковые ключи с числовыми значениями.


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

Object может комбинироваться с другими типами:

import { object, string, array, number } from 'superstruct'

const Team = object({
  name: string(),
  members: array(
    object({
      id: number(),
      name: string(),
    })
  ),
})

Такие структуры часто используются для описания API-ответов.


Поведение при неизвестных полях

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

Типичная стратегия обработки:

  • игнорирование лишних полей при чтении
  • фильтрация данных перед сохранением
  • явная ошибка при строгой валидации

Производительность объектной валидации

Объектная проверка выполняется последовательно по ключам. Производительность зависит от:

  • глубины вложенности
  • количества полей
  • сложности вложенных структур
  • наличия кастомных проверок (refine, coerce)

Оптимизация достигается через переиспользование схем и минимизацию избыточных проверок.


Типовые сценарии применения

Объектные структуры в Superstruct применяются для:

  • валидации входящих API-запросов
  • проверки конфигурационных файлов
  • нормализации пользовательских данных
  • построения схем DTO в сервисной архитектуре
  • контроля форм на клиентской стороне

Композиция как основной принцип

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