Числа: number()

Тип number() в Joi используется для строгой и гибкой валидации числовых значений. Он позволяет проверять не только факт того, что значение является числом, но и накладывать дополнительные ограничения: диапазоны, целочисленность, знаки, точность и математические свойства. В контексте серверной валидации это один из наиболее часто применяемых типов, поскольку числовые данные встречаются практически в любом API.


Схема начинается с создания числового валидатора:

const Joi = require('joi');

const schema = Joi.number();

По умолчанию Joi пытается привести входное значение к числу. Это поведение называется coercion (приведение типов). Например, строка "42" будет успешно преобразована в число 42.


Строгий режим без приведения типов

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

const schema = Joi.number().strict();

Теперь значение "42" уже не пройдет проверку, так как это строка, а не число.


Проверка целых чисел

Метод integer() ограничивает значения только целыми числами:

const schema = Joi.number().integer();

Примеры поведения:

  • 10 — допустимо
  • 10.5 — ошибка
  • "10" — допустимо при включенном преобразовании типов

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

Для числовых диапазонов используются методы min() и max():

const schema = Joi.number().min(0).max(100);

Такой валидатор пропустит значения от 0 до 100 включительно.

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

  • -1 — меньше допустимого минимума
  • 101 — превышает максимум

Строгое сравнение: greater и less

Для более точного контроля используются:

const schema = Joi.number().greater(0).less(100);

В отличие от min и max, здесь границы не включаются:

  • 0 — не допускается
  • 100 — не допускается

Работа с положительными и отрицательными числами

Joi предоставляет удобные методы для проверки знака числа:

Joi.number().positive();
Joi.number().negative();
  • positive() допускает только числа больше 0
  • negative() допускает только числа меньше 0

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

Метод multiple() позволяет проверить делимость числа:

const schema = Joi.number().multiple(5);

Допустимые значения: 0, 5, 10, 15, 20

Недопустимые: 3, 7, 12


Контроль точности дробных чисел

Метод precision() ограничивает количество знаков после запятой:

const schema = Joi.number().precision(2);

Примеры:

  • 10.12 — допустимо
  • 10.123 — ошибка
  • 10 — допустимо

Обработка специальных чисел: NaN и Infinity

По умолчанию Joi не допускает NaN и бесконечности:

Joi.number().allow(Infinity, -Infinity);

Или отдельно:

Joi.number().valid(Number.POSITIVE_INFINITY);

Однако в большинстве API такие значения считаются некорректными и запрещаются.


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

Числовое поле может быть обязательным:

const schema = Joi.number().required();

Или допускающим отсутствие:

const schema = Joi.number().optional();

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

Если число отсутствует, можно задать стандартное значение:

const schema = Joi.number().default(0);

При отсутствии поля в объекте оно автоматически получит значение 0.


Допустимые значения (whitelist)

Можно ограничить набор допустимых чисел:

const schema = Joi.number().valid(1, 2, 3, 5, 8);

Любое другое число вызовет ошибку валидации.


Пользовательские сообщения об ошибках

Joi позволяет переопределять текст ошибок:

const schema = Joi.number().min(0).messages({
  'number.min': 'Число не может быть отрицательным'
});

Комбинирование правил

Сила Joi заключается в комбинировании ограничений:

const schema = Joi.number()
  .integer()
  .min(10)
  .max(100)
  .positive()
  .multiple(2);

Такой валидатор пропустит только:

  • целые числа
  • больше 10
  • меньше или равные 100
  • положительные
  • кратные 2

Приведение строк к числам

Joi автоматически преобразует строки:

Joi.number().validate("123"); // 123

Однако это работает только если строка строго соответствует числу. Значения вроде "123abc" вызовут ошибку.


Поведение с null и undefined

  • undefined игнорируется, если поле не required
  • null по умолчанию запрещён

Разрешение null:

Joi.number().allow(null);

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

Чаще всего number() применяется внутри схем объектов:

const schema = Joi.object({
  age: Joi.number().integer().min(0).max(120),
  price: Joi.number().precision(2).positive(),
  rating: Joi.number().min(1).max(5)
});

Важные особенности поведения

  • Преобразование типов включено по умолчанию
  • Ошибки зависят от конкретного нарушения правила
  • Порядок методов влияет на итоговую логику
  • Некоторые правила могут конфликтовать (например, positive() и negative() одновременно)

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

Частые проблемы при работе с number():

  • ожидание строгой типизации без strict()
  • забытое ограничение integer() при работе с идентификаторами
  • некорректная обработка null
  • отсутствие диапазона min/max для пользовательских значений
  • использование valid() вместо диапазона, где требуется гибкость

Рекомендованные практики

  • использовать strict() для финансовых и критичных данных
  • всегда задавать min/max для пользовательского ввода
  • избегать неограниченных чисел в публичных API
  • применять precision() для денежных значений
  • явно определять поведение null и undefined