Библиотека class-validator предоставляет набор
декораторов для декларативной валидации свойств классов. Среди числовых
валидаторов особое место занимают @IsPositive и
@IsNegative, предназначенные для проверки знака числового
значения. Эти декораторы применяются к числовым полям DTO-моделей и
позволяют строго ограничивать допустимые значения на уровне модели
данных, до попадания в бизнес-логику.
Числовая валидация в class-validator опирается на идею
декларативного описания ограничений прямо в классе. Вместо ручной
проверки значений используется набор правил, привязанных к полям через
декораторы.
Для чисел библиотека предоставляет такие базовые проверки, как:
@IsNumber)@Min, @Max)@IsPositive,
@IsNegative)Декораторы @IsPositive и @IsNegative
относятся к группе ограничений по знаку числа и работают поверх уже
корректного числового значения.
@IsPositive проверяет, что значение строго больше нуля.
Ноль не считается положительным числом и не проходит валидацию.
Валидация проходит успешно, если:
Валидация проваливается, если:
import { IsPositive } from 'class-validator';
export class CreateProductDto {
@IsPositive()
price: number;
}
В данном примере поле price должно быть строго
положительным числом. Попытка передать 0 или отрицательное
значение приведёт к ошибке валидации.
| Значение | Результат |
|---|---|
| 10 | проходит |
| 0 | ошибка |
| -5 | ошибка |
| 3.14 | проходит |
| “10” | ошибка (без transform) |
При использовании ValidationPipe в рамках NestJS или
аналогичных механизмов, часто включается преобразование типов:
new ValidationPipe({
transform: true
})
В этом случае строковые значения могут быть преобразованы в числа
перед валидацией. Тогда "10" будет преобразовано в
10 и успешно пройдёт проверку @IsPositive.
@IsNegative проверяет, что значение строго меньше нуля.
Ноль не считается отрицательным значением.
Валидация проходит успешно, если:
Валидация проваливается, если:
import { IsNegative } from 'class-validator';
export class TransactionDto {
@IsNegative()
balanceChange: number;
}
Здесь поле balanceChange допускает только отрицательные
значения, например списания или корректировки в сторону уменьшения.
| Значение | Результат |
|---|---|
| -10 | проходит |
| -0.5 | проходит |
| 0 | ошибка |
| 15 | ошибка |
| ” -10 ” | ошибка (без transform) |
Оба декоратора реализуют простую проверку сравнения:
@IsPositive эквивалентен проверке
value > 0@IsNegative эквивалентен проверке
value < 0При этом библиотека дополнительно:
NaN как валидное числоclass-transformerВ реальных DTO почти всегда используется комбинирование декораторов:
import { IsNumber, IsPositive } from 'class-validator';
export class PaymentDto {
@IsNumber()
@IsPositive()
amount: number;
}
@IsPositive не гарантирует, что значение является
числом. Он лишь проверяет знак. Поэтому без @IsNumber
возможны некорректные состояния, особенно при отключённой
трансформации:
true > 0 → может вести себя непредсказуемо"" > 0 → приводит к странным результатам в
JavaScriptКомбинация обеспечивает строгую типовую защиту.
Особое внимание требуется при работе с нечисловыми значениями JavaScript.
@IsPositive()
value: number;
NaN всегда считается невалиднымNaN > 0 возвращает falseInfinity проходит @IsPositive-Infinity проходит @IsNegativeЭто связано с семантикой JavaScript сравнений, а не с логикой библиотеки.
export class WithdrawDto {
@IsNegative()
amount: number;
}
Применение @IsNegative позволяет явно ограничить
направление движения средств — только списание.
export class ScoreAdjustmentDto {
@IsPositive()
bonus: number;
@IsNegative()
penalty: number;
}
Такое разделение позволяет строго контролировать влияние операций на счёт игрока.
@IsPositive применяется к длинам, массам, времени@IsNegative — к смещениям, потерям, отрицательным
изменениямПо умолчанию class-validator возвращает стандартные
сообщения:
value must be a positive numbervalue must be a negative numberСообщения можно переопределять:
@IsPositive({ message: 'Значение должно быть больше нуля' })
price: number;
Или с параметризацией:
@IsNegative({ message: 'Поле $property должно быть отрицательным' })
Оба декоратора строго исключают ноль:
0 не положительное0 не отрицательноеЭто часто требует дополнительной логики, если ноль допустим:
import { Min } from 'class-validator';
@Min(0)
value: number;
@IsPositive и @IsNegative не дают
диапазонного контроля. Они не заменяют:
@Min@MaxНапример:
@IsPositive()
@Max(100)
value: number;
При валидации выполняются все декораторы свойства. Ошибки могут возникать от любого из них.
@IsNumber()
@IsPositive()
@Max(50)
value: number;
Логика проверки:
При работе с массивами требуется дополнительный декоратор:
import { IsPositive, IsNumber, IsArray } from 'class-validator';
export class BulkDto {
@IsArray()
@IsNumber({}, { each: true })
@IsPositive({ each: true })
values: number[];
}
Без each: true проверка будет применена к массиву как к
объекту, а не к элементам.
@IsPositive()
value: number;
Проблема: строка "10" может пройти преобразование
неожиданным образом или привести к некорректной логике.
Частая ошибка — ожидание, что 0 считается положительным
или отрицательным.
Без включённого transform валидация может работать с
сырыми строками из запроса:
ValidationPipe({
transform: false
})
В таком режиме входные данные остаются строками, что снижает предсказуемость результата.
import { IsNumber, IsPositive, IsNegative, IsOptional } from 'class-validator';
export class AccountAdjustmentDto {
@IsNumber()
@IsPositive()
credit: number;
@IsNumber()
@IsNegative()
debit: number;
@IsOptional()
@IsNumber()
@IsPositive()
bonus?: number;
}
Такая структура позволяет строго разделить финансовые операции по знаку и избежать неоднозначности на уровне входных данных.