Enums

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


Базовая структура enum

В Superstruct перечисление создаётся через функцию enums, которой передаётся массив допустимых значений:

import { enums } from 'superstruct'

const Status = enums(['idle', 'loading', 'success', 'error'])

Полученная структура проверяет, что значение строго совпадает с одним из элементов списка.

Примеры допустимых значений:

Status('idle')     // корректно
Status('success')  // корректно

Примеры ошибок:

Status('pending')  // ошибка валидации
Status('')         // ошибка валидации

Поведение валидации

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

Особенности:

  • сравнение выполняется через строгое равенство (===)
  • регистр символов имеет значение
  • пробелы и дополнительные символы приводят к ошибке
  • отсутствует частичное совпадение

Строковые перечисления

Наиболее распространённый вариант использования — строковые enum-структуры:

const Role = enums(['admin', 'editor', 'viewer'])

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

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

const User = object({
  name: string(),
  role: enums(['admin', 'editor', 'viewer'])
})

Валидация:

User({
  name: 'Alex',
  role: 'admin'
})

Числовые перечисления

Допускается использование чисел в качестве допустимых значений:

const HttpStatus = enums([200, 400, 401, 404, 500])

Проверка:

HttpStatus(200)  // корректно
HttpStatus(403)  // ошибка

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


Смешанные перечисления

Superstruct допускает смешанные наборы, содержащие строки и числа одновременно:

const Mixed = enums(['low', 1, 'high', 2])

Проверка строго сопоставляет тип и значение, поэтому '1' и 1 рассматриваются как разные элементы.


Использование в типизированных проектах (TypeScript)

При использовании TypeScript структура enums автоматически выводит объединённый тип:

import { enums } from 'superstruct'

const Direction = enums(['left', 'right', 'up', 'down'])

// тип:
// 'left' | 'right' | 'up' | 'down'

Это позволяет синхронизировать runtime-валидацию и compile-time типизацию без дополнительных объявлений.


Поведение ошибок

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

Пример:

Direction('forward')

Результат ошибки включает:

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

Интеграция с объектными структурами

Перечисления часто используются внутри сложных схем:

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

const Task = object({
  id: number(),
  title: string(),
  status: enums(['todo', 'in_progress', 'done']),
  priority: enums([1, 2, 3])
})

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


Поведение при вложенных структурах

При вложенности enum остаётся неизменным валидатором и применяется локально:

const Config = object({
  theme: object({
    mode: enums(['dark', 'light'])
  })
})

Ошибка будет указывать на конкретное поле theme.mode.


Сравнение с union-структурами

Перечисления можно рассматривать как специализированный случай объединения литералов. Однако enums имеет более лаконичный синтаксис и оптимизирован для фиксированных наборов.

Эквивалент через union:

import { union, literal } from 'superstruct'

const Status = union([
  literal('idle'),
  literal('loading'),
  literal('success')
])

Но enums предоставляет более компактную форму:

const Status = enums(['idle', 'loading', 'success'])

Отсутствие преобразований

Значения не приводятся автоматически к ожидаемому типу. Например:

const Flag = enums([1, 2, 3])

Flag('1') // ошибка

Даже при визуальной эквивалентности типов строка и число считаются различными значениями.


Использование валидации входных данных API

Перечисления часто применяются при проверке входящих данных API, где необходимо ограничить набор допустимых значений параметров:

const Query = object({
  sort: enums(['asc', 'desc']),
  page: number()
})

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


Повторяющиеся значения и уникальность

Массив значений должен содержать уникальные элементы. Дублирование приводит к логической неоднозначности и может нарушать ожидаемое поведение валидации:

enums(['a', 'b', 'a']) // некорректная конфигурация

Производительность

Перечисления оптимизированы под быстрый поиск через прямое сравнение. Проверка выполняется за линейное время относительно количества элементов, однако при небольших наборах (обычный кейс) это не оказывает заметного влияния на производительность.


Комбинирование с другими структурами

Enum-структуры часто комбинируются с optional, defaulted, nullable:

import { optional, enums } from 'superstruct'

const Mode = optional(enums(['auto', 'manual']))

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


Типичные ошибки использования

  • передача значений другого типа ('1' вместо 1)
  • попытка использовать динамически формируемые значения без фиксации списка
  • ожидание частичного совпадения строк
  • смешивание с неконсистентными типами в одном наборе

Поведение при сериализации данных

Enum не изменяет данные и не выполняет трансформации. Он работает исключительно как слой проверки. Входное значение возвращается без модификации при успешной валидации:

const value = Status('idle') // возвращает 'idle'