@Equals

Декоратор @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.


Проверка null

import { Equals } from 'class-validator';

class ExampleDto {
  @Equals(null)
  deletedAt: null;
}

Подойдут только значения:

deletedAt = null;

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


Проверка undefined

import { 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'
}

Использование функции в message

import { Equals } from 'class-validator';

class UserDto {
  @Equals('admin', {
    message: (args) => {
      return `Поле ${args.property} должно быть равно admin`;
    }
  })
  role: string;
}

Работа с ValidationArguments

import {
  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 Аргументы декоратора

Использование с DTO в NestJS

Часто @Equals применяется внутри DTO в NestJS.

import { Equals } from 'class-validator';

export class AuthDto {
  @Equals('local')
  provider: string;
}

Если клиент отправит:

{
  "provider": "google"
}

валидация завершится ошибкой.


Комбинация с @IsString

import {
  Equals,
  IsString
} from 'class-validator';

class UserDto {
  @IsString()
  @Equals('admin')
  role: string;
}

Проверки выполняются последовательно:

  1. значение должно быть строкой;
  2. строка должна быть равна "admin".

Комбинация с @IsNotEmpty

import {
  Equals,
  IsNotEmpty
} from 'class-validator';

class ConfigDto {
  @IsNotEmpty()
  @Equals('production')
  mode: string;
}

Использование с @ValidateIf

import {
  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')

Значение не должно совпадать.


Проверка фиксированных служебных значений

API-версия

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-transformer

import { 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

Подходящие сценарии:

  • фиксированные системные значения;
  • обязательные служебные поля;
  • контроль версии API;
  • проверка типа события;
  • проверка источника запроса;
  • ограничение допустимого режима работы;
  • валидация специальных флагов.

Когда @Equals использовать не стоит

Неподходящие сценарии:

  • сравнение объектов;
  • сравнение массивов;
  • проверка диапазонов;
  • проверка шаблонов;
  • проверка частичного совпадения;
  • сложная бизнес-логика.

Практический пример DTO

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 Да
Объекты Нежелательно
Массивы Нежелательно
Строгое сравнение Да
Пользовательские сообщения Да
Асинхронность Нет
Наследование Да