Декоратор @Equals из библиотеки class-validator
предназначен для строгой проверки значения на полное соответствие
указанному эталону. Проверка выполняется через оператор строгого
сравнения ===.
Декоратор применяется в ситуациях, когда поле должно содержать только конкретное значение:
npm install class-validator class-transformer
Для поддержки декораторов требуется включить параметры в
tsconfig.json:
{
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
@Equals(value)
Также поддерживается объект настроек:
@Equals(value, validationOptions)
import { validate } from 'class-validator';
import { Equals } from 'class-validator';
class UserDto {
@Equals('admin')
role: string;
}
async function run() {
const dto = new UserDto();
dto.role = 'admin';
console.log(await validate(dto));
}
Результат:
[]
Ошибок нет, потому что значение совпадает.
import { validate } from 'class-validator';
import { Equals } from 'class-validator';
class UserDto {
@Equals('admin')
role: string;
}
async function run() {
const dto = new UserDto();
dto.role = 'user';
console.log(await validate(dto));
}
Результат:
[
ValidationError {
property: 'role',
constraints: {
equals: 'role must be equal to admin'
}
}
]
import { Equals } from 'class-validator';
class ProductDto {
@Equals(100)
price: number;
}
Допустимое значение:
dto.price = 100;
Недопустимое:
dto.price = 150;
import { Equals } from 'class-validator';
class SettingsDto {
@Equals(true)
enabled: boolean;
}
Поле обязано содержать именно true.
nullimport { Equals } from 'class-validator';
class ExampleDto {
@Equals(null)
deletedAt: null;
}
Подойдут только значения:
deletedAt = null;
Любое другое значение вызовет ошибку.
undefinedimport { Equals } from 'class-validator';
class ExampleDto {
@Equals(undefined)
value: undefined;
}
Используется редко, но иногда полезно при строгом контроле DTO.
@Equals использует оператор:
===
Это означает:
1 !== '1'
Пример:
import { Equals } from 'class-validator';
class ExampleDto {
@Equals(1)
value: number;
}
Проверка:
dto.value = '1';
Завершится ошибкой, поскольку строка и число — разные типы.
enum UserRole {
ADMIN = 'admin',
USER = 'user'
}
class UserDto {
@Equals(UserRole.ADMIN)
role: UserRole;
}
Поле может содержать только:
'admin'
import { Equals } from 'class-validator';
class UserDto {
@Equals('admin', {
message: 'Роль должна быть admin'
})
role: string;
}
Результат ошибки:
{
equals: 'Роль должна быть admin'
}
messageimport { Equals } from 'class-validator';
class UserDto {
@Equals('admin', {
message: (args) => {
return `Поле ${args.property} должно быть равно admin`;
}
})
role: string;
}
ValidationArgumentsimport {
Equals,
ValidationArguments
} from 'class-validator';
class UserDto {
@Equals('admin', {
message: (args: ValidationArguments) => {
console.log(args.value);
console.log(args.property);
console.log(args.targetName);
return 'Ошибка валидации';
}
})
role: string;
}
Содержимое ValidationArguments:
| Свойство | Описание |
|---|---|
value |
Текущее значение |
property |
Имя поля |
targetName |
Имя класса |
constraints |
Аргументы декоратора |
Часто @Equals применяется внутри DTO в NestJS.
import { Equals } from 'class-validator';
export class AuthDto {
@Equals('local')
provider: string;
}
Если клиент отправит:
{
"provider": "google"
}
валидация завершится ошибкой.
@IsStringimport {
Equals,
IsString
} from 'class-validator';
class UserDto {
@IsString()
@Equals('admin')
role: string;
}
Проверки выполняются последовательно:
"admin".@IsNotEmptyimport {
Equals,
IsNotEmpty
} from 'class-validator';
class ConfigDto {
@IsNotEmpty()
@Equals('production')
mode: string;
}
@ValidateIfimport {
Equals,
ValidateIf
} from 'class-validator';
class PaymentDto {
paymentType: string;
@ValidateIf(o => o.paymentType === 'card')
@Equals('approved')
status: string;
}
Проверка выполняется только для карточных платежей.
@NotEqualsВ библиотеке существует противоположный декоратор —
@NotEquals.
@Equals@Equals('admin')
Значение обязано совпадать.
@NotEquals@NotEquals('admin')
Значение не должно совпадать.
class RequestDto {
@Equals('v1')
apiVersion: string;
}
class EventDto {
@Equals('USER_CREATED')
type: string;
}
class ImportDto {
@Equals('crm')
source: string;
}
class FeatureDto {
@Equals(true)
betaEnabled: boolean;
}
class MessageDto {
@Equals('orders-service')
sender: string;
}
Такой подход позволяет отклонять сообщения от неизвестных источников.
class AppConfigDto {
@Equals('production')
environment: string;
}
Важно учитывать особенности строгого сравнения объектов.
class ExampleDto {
@Equals({ active: true })
settings: object;
}
Такой код почти всегда бесполезен.
Причина:
{} === {}
возвращает:
false
Потому что сравниваются ссылки на объекты, а не содержимое.
@Equals([1, 2, 3])
Массивы также сравниваются по ссылке.
[1,2,3] === [1,2,3]
Результат:
false
Для массивов и объектов лучше использовать пользовательские валидаторы.
@Equals(1)
Ошибка:
dto.value = '1';
@Equals({
role: 'admin'
})
Такой подход не работает ожидаемым образом.
При работе с HTTP-запросами значения часто приходят строками.
{
"value": "1"
}
Если ожидается число:
@Equals(1)
валидация завершится ошибкой.
class-transformerimport { Type } from 'class-transformer';
import { Equals } from 'class-validator';
class ExampleDto {
@Type(() => Number)
@Equals(1)
value: number;
}
Теперь строка:
{
"value": "1"
}
будет преобразована в число.
class ExampleDto {
@IsString()
@Equals('admin')
role: string;
}
Сначала выполняется @IsString, затем
@Equals.
class BaseDto {
@Equals('v1')
version: string;
}
class UserDto extends BaseDto {
name: string;
}
Валидатор наследуется автоматически.
class MetaDto {
@Equals('system')
source: string;
}
class RequestDto {
meta: MetaDto;
}
Обычно используется вместе с:
@ValidateNested()
и @Type.
@Equals не выполняет асинхронных операций. Проверка
всегда синхронная и очень быстрая.
Декоратор практически не создаёт нагрузки:
@Equals считается одним из самых лёгких валидаторов в
библиотеке.
@EqualsПодходящие сценарии:
@Equals
использовать не стоитНеподходящие сценарии:
import {
Equals,
IsString,
IsUUID
} from 'class-validator';
export class CreateEventDto {
@IsUUID()
id: string;
@IsString()
@Equals('USER_CREATED')
event: string;
@IsString()
@Equals('v1')
version: string;
}
Допустимый запрос:
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"event": "USER_CREATED",
"version": "v1"
}
Недопустимый:
{
"id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"event": "USER_UPDATED",
"version": "v2"
}
| Возможность | Поддержка |
|---|---|
| Строки | Да |
| Числа | Да |
| Boolean | Да |
null |
Да |
undefined |
Да |
| Объекты | Нежелательно |
| Массивы | Нежелательно |
| Строгое сравнение | Да |
| Пользовательские сообщения | Да |
| Асинхронность | Нет |
| Наследование | Да |