Number

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


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

Основная функция для числовой проверки создаётся через number():

import { number, validate } fr om 'superstruct'

const Age = number()

validate(25, Age) // ok
validate('25', Age) // ошибка
validate(NaN, Age) // ошибка

Ключевое поведение:

  • допускаются только значения типа number
  • исключаются строки, даже если они содержат числа
  • NaN считается невалидным значением
  • Infinity и -Infinity также не проходят проверку

Таким образом, number() обеспечивает строгую защиту от неявных преобразований, характерных для JavaScript.


Проверка диапазонов значений

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

import { number, min, max, validate } from 'superstruct'

const Positive = min(number(), 1)

const SmallRange = max(min(number(), 10), 100)

validate(5, Positive)   // ok
validate(0, Positive)   // ошибка

validate(50, SmallRange) // ok
validate(150, SmallRange) // ошибка

Логика ограничений:

  • min(struct, value) задаёт нижнюю границу включительно
  • max(struct, value) задаёт верхнюю границу включительно
  • ограничения можно комбинировать в любом порядке

Диапазоны применяются для:

  • возрастных значений
  • цен
  • координат
  • лимитов и квот

Целые числа

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

import { number, refine, validate } from 'superstruct'

const Integer = refine(number(), 'integer', (value) =>
  Number.isInteger(value)
)

validate(10, Integer)    // ok
validate(10.5, Integer)  // ошибка

Характеристики:

  • проверка выполняется в рантайме
  • используется Number.isInteger
  • не допускает строковое представление чисел

Типичные области применения:

  • индексы массивов
  • количество элементов
  • идентификаторы без дробной части

Проверка конечных значений

В JavaScript числовая система включает специальные значения Infinity и NaN. Superstruct требует явного контроля таких случаев.

import { number, refine } from 'superstruct'

const FiniteNumber = refine(number(), 'finite', (value) =>
  Number.isFinite(value)
)

Такой подход исключает:

  • NaN
  • Infinity
  • -Infinity

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


Преобразование типов и строгая модель данных

Superstruct по умолчанию не выполняет неявные преобразования. Это принципиальное отличие от слабой типизации JavaScript.

validate('42', number()) // ошибка
validate('42' * 1, number()) // ok (результат операции)

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

const toNumber = (value) => {
  const parsed = Number(value)
  return Number.isFinite(parsed) ? parsed : value
}

Далее уже применяется структура:

const Value = number()

Такой подход отделяет этап нормализации данных от этапа валидации.


Композиция с refine для сложных правил

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

import { number, refine } from 'superstruct'

const EvenNumber = refine(number(), 'even', (value) =>
  value % 2 === 0
)

Примеры расширений:

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

Композиция позволяет строить доменные типы:

const Price = refine(number(), 'price', (value) =>
  value >= 0 && value <= 1000000
)

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

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

import { number, optional } from 'superstruct'

const Schema = {
  lim it: optional(number())
}

Поведение:

  • поле может отсутствовать
  • при наличии проверяется как number
  • null не считается допустимым значением

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

const withDefault = (value, fallback) =>
  value === undefined ? fallback : value

Обработка ошибок в числовых структурах

При неуспешной проверке Superstruct возвращает структурированную ошибку.

import { number, validate } from 'superstruct'

const [error, result] = validate('abc', number())

Типичные причины ошибок:

  • передана строка вместо числа
  • значение NaN
  • значение выходит за пределы диапазона
  • нарушено пользовательское правило refine

Ошибки удобно использовать для:

  • валидации форм
  • API ответов
  • логирования некорректных входных данных

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

Конфигурации приложения

const Config = {
  port: number(),
  timeout: number()
}

REST API параметры

const Query = {
  page: number(),
  pageSize: number()
}

Финансовые вычисления

const Amount = refine(number(), 'amount', (v) => v >= 0)

Сочетание с объектами и массивами

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

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

const Model = object({
  id: number(),
  values: array(number())
})

Такой подход обеспечивает строгую типизацию вложенных данных:

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

Поведение в пограничных случаях

Особое внимание уделяется нестандартным числовым значениям Jav * aScript:

  • NaN всегда невалиден
  • Infinity и -Infinity исключаются
  • new Number(5) может приводиться к primitive, но не гарантируется валидацией без приведения
  • пустые строки не интерпретируются как ноль

Такая строгость исключает скрытые ошибки, возникающие из-за автоматического приведения типов в JavaScript-экосистеме.