Min и max для чисел

Библиотека Superstruct предоставляет встроенные механизмы для проверки числовых диапазонов. Наиболее распространённые сценарии:

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

Для этих задач используются функции min() и max() из модуля superstruct.


Подключение

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

Базовый тип:

const Age = number()

Без дополнительных ограничений структура принимает любое число:

42
-10
3.14
0

Минимальное значение: min

Функция min() задаёт нижнюю границу допустимого значения.

Синтаксис

min(struct, threshold)

Где:

  • struct — исходная структура;
  • threshold — минимально допустимое число.

Проверка минимального значения

import { assert, number, min } from 'superstruct'

const Age = min(number(), 18)

assert(21, Age)

Проверка проходит успешно.


Ошибка при нарушении ограничения

assert(15, Age)

Ошибка:

StructError: Expected a number greater than or equal to 18 but received `15`

Проверка возраста пользователя

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

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

Корректные данные:

{
  name: 'Alex',
  age: 25
}

Некорректные данные:

{
  name: 'Tom',
  age: 16
}

Минимальное значение для цены

const Price = min(number(), 0)

Подходит для:

  • стоимости товаров;
  • баланса;
  • количества;
  • числовых счётчиков.

Примеры:

0
10
999

Недопустимо:

-5

Ограничение отрицательных чисел

const Temperature = min(number(), -50)

Теперь значения ниже -50 будут отклоняться.


Максимальное значение: max

Функция max() задаёт верхнюю границу допустимого числа.

Синтаксис

max(struct, threshold)

Проверка максимального значения

import { assert, number, max } from 'superstruct'

const Percent = max(number(), 100)

assert(80, Percent)

Ошибка превышения лимита

assert(150, Percent)

Ошибка:

StructError: Expected a number less than or equal to 100 but received `150`

Ограничение рейтинга

const Rating = max(number(), 5)

Подходящие значения:

1
3
5

Недопустимо:

10

Комбинирование min и max

Обе функции можно объединять для задания диапазона.

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

const Score = max(
  min(number(), 0),
  100
)

Допустимые значения:

0
50
100

Недопустимые:

-1
101

Более читаемый вариант

При большом количестве ограничений вложенность ухудшает читаемость.

Пример:

const Score = max(min(number(), 0), 100)

Часто используют промежуточные переменные:

const PositiveNumber = min(number(), 0)

const Score = max(PositiveNumber, 100)

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

const ProductPrice = max(
  min(number(), 1),
  100000
)

Такой подход предотвращает:

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

Валидация процента

const Percentage = max(
  min(number(), 0),
  100
)

Примеры:

0
25
75
100

Ошибка:

-10
150

Ограничение количества товаров

const Quantity = max(
  min(number(), 1),
  1000
)

Полезно для:

  • корзины интернет-магазина;
  • складского учёта;
  • систем бронирования.

Работа с validate

assert() выбрасывает исключение. Для более безопасной проверки применяется validate().

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

const Age = min(number(), 18)

const [error, value] = validate(16, Age)

console.log(error)

Результат:

StructError: Expected a number greater than or equal to 18 but received `16`

Проверка без исключений

const [error, value] = validate(25, Age)

console.log(error) // undefined
console.log(value) // 25

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

Чаще всего ограничения применяются внутри объектов.

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

const Product = object({
  title: string(),

  price: max(
    min(number(), 1),
    100000
  ),

  stock: max(
    min(number(), 0),
    5000
  ),
})

Проверка данных формы

const RegisterForm = object({
  age: min(number(), 18),
})

Некорректные данные:

{
  age: 14
}

Ограничение размера скидки

const Discount = max(
  min(number(), 0),
  90
)

Логика:

  • скидка не может быть отрицательной;
  • скидка не должна достигать 100%.

Проверка длины кредита

const CreditMonths = max(
  min(number(), 1),
  360
)

Валидация координат

Широта

const Latitude = max(
  min(number(), -90),
  90
)

Долгота

const Longitude = max(
  min(number(), -180),
  180
)

Проверка размера файла

const FileSize = max(
  min(number(), 1),
  10 * 1024 * 1024
)

Пример ограничения файла до 10 МБ.


Проверка времени выполнения

const Timeout = max(
  min(number(), 100),
  30000
)

Подход предотвращает:

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

Вложенные структуры

const GameSettings = object({
  graphics: object({
    fps: max(
      min(number(), 30),
      240
    ),
  }),
})

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

import {
  optional,
  number,
  min
} from 'superstruct'

const Discount = optional(
  min(number(), 0)
)

Допустимо:

undefined
10
25

Недопустимо:

-5

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

import {
  defaulted,
  number,
  min
} from 'superstruct'

const Attempts = defaulted(
  min(number(), 1),
  3
)

Если значение отсутствует, используется 3.


Особенности проверки

min() включает границу

min(number(), 10)

Допустимо:

10
11
12

max() включает границу

max(number(), 100)

Допустимо:

100
99
50

Проверка дробных чисел

min() и max() работают как с целыми, так и с дробными значениями.

const Temperature = max(
  min(number(), -10.5),
  45.8
)

Проверка Infinity

const Value = number()

Infinity технически является числом в JavaScript.

assert(Infinity, Value)

Проверка пройдёт успешно.

Для ограничения диапазона необходимо использовать max().

const SafeValue = max(number(), 1000000)

Проверка NaN

assert(NaN, number())

NaN не проходит проверку number() в Superstruct.


Практический пример

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

const Employee = object({
  name: string(),

  age: max(
    min(number(), 18),
    65
  ),

  salary: min(number(), 0),
})

const data = {
  name: 'John',
  age: 70,
  salary: 5000,
}

const [error, value] = validate(data, Employee)

if (error) {
  console.log(error.message)
}

Результат:

Expected a number less than or equal to 65 but received `70`

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

Финансы

const Amount = min(number(), 0)

Проценты

const Percent = max(
  min(number(), 0),
  100
)

Возраст

const Age = max(
  min(number(), 0),
  120
)

Рейтинг

const Rating = max(
  min(number(), 1),
  5
)

Ограничение FPS

const FPS = max(
  min(number(), 30),
  240
)

Частые ошибки

Неправильный порядок аргументов

Неверно:

min(18, number())

Правильно:

min(number(), 18)

Отсутствие number()

Неверно:

min(18)

Правильно:

min(number(), 18)

Повторное создание структуры

Нежелательно:

validate(value, max(min(number(), 0), 100))

Лучше создавать структуру один раз:

const Score = max(
  min(number(), 0),
  100
)

validate(value, Score)

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

Структуры Superstruct рекомендуется переиспользовать.

Плохой вариант:

function validateAge(age) {
  return validate(age, min(number(), 18))
}

Лучше:

const Age = min(number(), 18)

function validateAge(age) {
  return validate(age, Age)
}

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


Комбинация с refine

min() и max() удобно комбинировать с пользовательскими правилами.

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

const EvenNumber = refine(
  max(
    min(number(), 0),
    100
  ),
  'EvenNumber',
  value => value % 2 === 0
)

Допустимо:

2
20
100

Недопустимо:

3
101
-2