Валидация чисел

В библиотеке Class-validator числовая валидация строится на наборе специализированных декораторов, обеспечивающих проверку типа, диапазонов, знака и дополнительных ограничений. В отличие от простых проверок JavaScript, где типы могут неявно приводиться, здесь контроль основан на явной типизации DTO-классов и метаданных, формируемых декораторами.


Проверка числового типа: @IsNumber

Базовый декоратор для проверки числового значения:

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

class ProductDto {
  @IsNumber()
  price: number;
}

Поведение по умолчанию

@IsNumber() проверяет строгое соответствие типу number. При этом строковые значения вроде "123" считаются невалидными.

Параметры IsNumberOptions

Декоратор поддерживает настройки, влияющие на строгость проверки:

@IsNumber({}, { allowNaN: false, allowInfinity: false })
value: number;

Основные опции:

  • allowNaN — разрешает NaN
  • allowInfinity — разрешает Infinity и -Infinity

Пример с расширенной допустимостью:

@IsNumber({}, { allowNaN: true, allowInfinity: true })
metric: number;

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

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

import { IsInt } from 'class-validator';

class UserDto {
  @IsInt()
  age: number;
}

Особенности

  • запрещает числа с плавающей точкой
  • допускает отрицательные и положительные значения
  • не выполняет приведение типов

Пример невалидных значений:

  • 12.5
  • "10"
  • true

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

Положительные значения

import { IsPositive } from 'class-validator';

class PaymentDto {
  @IsPositive()
  amount: number;
}

Допускаются только значения строго больше 0.


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

import { IsNegative } from 'class-validator';

class TemperatureDto {
  @IsNegative()
  value: number;
}

Допускаются только значения меньше 0.


Неотрицательные и неположительные значения

import { IsNotNegative, IsNotPositive } from 'class-validator';

class RangeDto {
  @IsNotNegative()
  min: number;

  @IsNotPositive()
  max: number;
}
  • IsNotNegative>= 0
  • IsNotPositive<= 0

Ограничение диапазона: @Min и @Max

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

import { Min } from 'class-validator';

class ScoreDto {
  @Min(0)
  score: number;
}

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

import { Max } from 'class-validator';

class ScoreDto {
  @Max(100)
  score: number;
}

Комбинирование ограничений

class ProgressDto {
  @Min(0)
  @Max(1)
  progress: number;
}

Частая проблема: числовые строки

В реальных HTTP-запросах числа часто приходят как строки:

{
  "price": "150"
}

Без преобразования такие значения не пройдут @IsNumber().


Преобразование типов

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

import { Type } from 'class-transformer';
import { IsNumber } from 'class-validator';

class ProductDto {
  @Type(() => Number)
  @IsNumber()
  price: number;
}

Роль @Type(() => Number)

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

Валидация числовых строк: @IsNumberString

Для случаев, когда значение должно оставаться строкой, но содержать число:

import { IsNumberString } from 'class-validator';

class QueryDto {
  @IsNumberString()
  page: string;
}

Особенности

  • допускаются только строки, состоящие из цифр
  • не допускаются NaN, пробелы, символы
  • полезно для query-параметров URL

Граничные случаи и поведение

NaN и Infinity

@IsNumber()
value: number;
  • NaN по умолчанию считается невалидным
  • Infinity и -Infinity также отклоняются без опций

Пустые значения

null и undefined не проходят числовую валидацию, если не используются дополнительные декораторы:

  • @IsOptional() — допускает отсутствие поля
  • без него значение считается обязательным
import { IsOptional, IsNumber } from 'class-validator';

class FilterDto {
  @IsOptional()
  @IsNumber()
  lim it?: number;
}

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

Числовые валидаторы часто комбинируются с другими ограничениями:

import { IsInt, Min, Max, IsPositive } from 'class-validator';

class RatingDto {
  @IsInt()
  @Min(1)
  @Max(5)
  rating: number;
}

или

class AccountDto {
  @IsPositive()
  @IsNumber()
  balance: number;
}

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

Отсутствие трансформации

class Dto {
  @IsNumber()
  value: number;
}

Вход "42" приведёт к ошибке, даже если логически значение корректно.


Использование @IsNumber вместо @IsInt

Для идентификаторов часто требуется строгое целое число:

class ParamDto {
  @IsInt()
  id: number;
}

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


Игнорирование диапазонов

Отсутствие @Min и @Max приводит к тому, что валидируется только тип, но не смысл значения:

class DiscountDto {
  @IsNumber()
  percent: number; // логически должен быть 0–100
}

Корректная форма:

class DiscountDto {
  @Min(0)
  @Max(100)
  percent: number;
}

Валидация в связке с DTO-структурами

Числовые валидаторы раскрываются полноценно только в строгой DTO-модели, где:

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

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