@IsDivisibleBy

Декоратор @IsDivisibleBy из библиотеки class-validator применяется для проверки числового значения на кратность заданному делителю. В основе лежит строгая математическая проверка остатка от деления: значение считается валидным, если остаток равен нулю.

Ключевая особенность — ориентация исключительно на числовую семантику. Проверка не выполняет преобразований типов и не пытается интерпретировать строковые значения как числа без дополнительной трансформации.


Сигнатура и базовое использование

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

IsDivisibleBy(value: number, validationOptions?: ValidationOptions)
  • value — число, на которое должно делиться проверяемое значение
  • validationOptions — стандартные опции class-validator (сообщения, группы, условия применения)

Простейшая форма:

import { IsDivisibleBy } fr om 'class-validator';

class ExampleDto {
  @IsDivisibleBy(5)
  amount: number;
}

В данном случае значение amount считается корректным только если оно кратно 5.


Математическая модель проверки

Логика проверки базируется на выражении:

[ value divisor = 0]

где:

  • value — проверяемое число
  • divisor — аргумент декоратора

Эквивалентная логика на уровне реализации:

value % divisor === 0

При этом используется поведение JavaScript-оператора %, включая особенности работы с отрицательными числами и дробными значениями.


Особенности работы с типами

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

Декоратор не выполняет приведение типов:

class ExampleDto {
  @IsDivisibleBy(3)
  value: number;
}
Входное значение Результат
9 валидно
10 невалидно
“9” невалидно
null невалидно
undefined невалидно

Для обработки строковых чисел требуется предварительная трансформация через class-transformer.


Работа с дробными числами

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

9.6 % 3 // 0.5999999999999996

Следовательно:

@IsDivisibleBy(3)
value: number;

значение 9.6 будет считаться невалидным.

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

  • проверка не округляет значения
  • не применяется epsilon-сравнение
  • используется строгое сравнение результата % с нулём

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

Поведение для отрицательных значений соответствует спецификации Jav * aScript:

-9 % 3 === 0

Следовательно:

Значение Делитель Результат
-9 3 валидно
-10 3 невалидно

Знак числа не влияет на кратность.


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

Типичный сценарий — валидация входных данных в транспортных объектах.

import { IsDivisibleBy, IsInt } from 'class-validator';

class PaginationDto {
  @IsInt()
  @IsDivisibleBy(10)
  lim it: number;
}

Здесь комбинирование ограничений усиливает семантику:

  • @IsInt() исключает дробные значения
  • @IsDivisibleBy(10) ограничивает шаг значений

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

@IsNumber

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

import { IsNumber, IsDivisibleBy } from 'class-validator';

class ExampleDto {
  @IsNumber()
  @IsDivisibleBy(4)
  value: number;
}

Без @IsNumber возможно прохождение некорректных данных при неправильной трансформации входа.


@IsOptional

При добавлении @IsOptional проверка делимости не выполняется для null и undefined:

class ExampleDto {
  @IsOptional()
  @IsDivisibleBy(2)
  value?: number;
}

Поведение при 0 и делителе

Особое внимание требует делитель:

@IsDivisibleBy(0)
value: number;

Деление на ноль в JavaScript приводит к особенностям вычисления %:

  • value % 0NaN
  • любое сравнение NaN === 0false

Следовательно, при делителе 0 все значения считаются невалидными.


Пограничные случаи

NaN

NaN % 3 // NaN

Любое значение NaN автоматически проваливает проверку.


Infinity

Infinity % 3 // NaN

Значения Infinity и -Infinity также считаются невалидными.


Очень большие числа

При работе с Number.MAX_SAFE_INTEGER возможны потери точности, что влияет на корректность остатка от деления.


Поведение внутри pipeline трансформации

При использовании class-transformer типичная цепочка выглядит следующим образом:

  1. входные данные приходят как строки
  2. transform преобразует их в числа
  3. class-validator выполняет проверку @IsDivisibleBy

Без трансформации:

class ExampleDto {
  @IsDivisibleBy(2)
  value: number;
}

вход "8" будет отклонён, поскольку тип остаётся строкой.


Влияние строгого режима валидации

При включении whitelist и forbidNonWhitelisted в NestJS-подобных системах @IsDivisibleBy остаётся исключительно проверочным механизмом и не влияет на структуру объекта, но участвует в общем результате валидации:

  • при ошибке возвращается стандартный ValidationError
  • сообщение формируется через ValidationOptions

Кастомизация сообщений

Возможна настройка текстов ошибок:

@IsDivisibleBy(3, {
  message: 'Значение должно быть кратно 3'
})
value: number;

Сообщение может зависеть от контекста через функции:

@IsDivisibleBy(5, {
  message: ({ value }) => `${value} не делится на 5 без остатка`
})

Производственные сценарии применения

Шаговые значения

Используется для контроля шагов в пагинации:

class QueryDto {
  @IsDivisibleBy(20)
  pageSize: number;
}

Финансовые ограничения

Ограничение кратности суммы:

class PaymentDto {
  @IsDivisibleBy(100)
  amount: number;
}

Тайминги и интервалы

class ScheduleDto {
  @IsDivisibleBy(15)
  intervalMinutes: number;
}

Ограничения и особенности реализации

  • не учитывается точность floating-point beyond стандартного поведения JS
  • отсутствует математическое округление
  • проверка зависит от результата оператора %
  • не выполняется нормализация типов
  • не применяется кастомная арифметика для больших чисел

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