Интерфейс ValidatorConstraintInterface

ValidatorConstraintInterface определяет контракт, который обязаны реализовать все пользовательские валидаторы в библиотеке Class-validator. Этот интерфейс лежит в основе механизма расширяемой валидации и позволяет описывать собственные правила проверки данных, интегрируемые в систему декораторов.


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

ValidatorConstraintInterface выступает формальным контрактом между библиотекой и пользовательской реализацией. Любой класс, объявленный как constraint, должен соответствовать этому интерфейсу, иначе он не сможет участвовать в процессе валидации.


Основная структура интерфейса

Интерфейс задаёт минимальный набор методов, необходимых для работы валидатора:

  • validate(value: any, args?: ValidationArguments): boolean | Promise
  • defaultMessage?(args?: ValidationArguments): string

Каждый метод имеет строго определённую роль в процессе проверки данных.


Метод validate

Назначение

Метод validate является центральной частью любого пользовательского constraint. Именно он содержит логику проверки значения.

Сигнатура

validate(value: any, args?: ValidationArguments): boolean | Promise<boolean>

Поведение

Метод должен вернуть:

  • true — если значение соответствует условиям валидатора
  • false — если значение не проходит проверку
  • Promise<boolean> — если проверка асинхронная (например, обращение к базе данных или API)

Аргументы

  • value — значение, которое проходит валидацию
  • args — объект ValidationArguments, содержащий дополнительный контекст

Объект ValidationArguments

Этот объект предоставляет метаинформацию о текущей проверке:

  • value — проверяемое значение
  • constraints — массив параметров, переданных в декоратор
  • targetName — имя класса, к которому относится проверка
  • object — экземпляр объекта, содержащего поле
  • property — имя свойства, к которому применён валидатор

Использование этих данных позволяет создавать контекстно-зависимую валидацию.


Метод defaultMessage

Назначение

Метод defaultMessage используется для генерации сообщения об ошибке, если validate возвращает false.

Сигнатура

defaultMessage?(args?: ValidationArguments): string

Особенности

  • Метод является опциональным
  • Если не реализован, используется стандартное сообщение библиотеки или сообщение, заданное в декораторе
  • Может использовать данные из ValidationArguments для динамического формирования текста ошибки

Связь с декоратором ValidatorConstraint

Реализация интерфейса ValidatorConstraintInterface обычно сопровождается декоратором:

@ValidatorConstraint({ name: 'customName', async: false })

Этот декоратор регистрирует класс как валидатор внутри системы Class-validator и связывает его с механизмом Dependency Injection (если используется соответствующая конфигурация).

Параметры декоратора:

  • name — уникальное имя constraint
  • async — флаг, указывающий, используется ли асинхронная валидация

Пример реализации пользовательского валидатора

import {
  ValidatorConstraint,
  ValidatorConstraintInterface,
  ValidationArguments
} from 'class-validator';

@ValidatorConstraint({ name: 'isEven', async: false })
class IsEvenConstraint implements ValidatorConstraintInterface {

  validate(value: number, args: ValidationArguments): boolean {
    return typeof value === 'number' && value % 2 === 0;
  }

  defaultMessage(args: ValidationArguments): string {
    return `Значение ${args.value} не является чётным числом`;
  }
}

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

После создания constraint он применяется через пользовательский декоратор:

import { Validate } from 'class-validator';

class SampleDto {
  @Validate(IsEvenConstraint)
  numberValue: number;
}

Декоратор Validate связывает поле с конкретной реализацией ValidatorConstraintInterface.


Асинхронные валидаторы

При необходимости проверки внешних данных используется асинхронная реализация:

@ValidatorConstraint({ name: 'isUnique', async: true })
class IsUniqueConstraint implements ValidatorConstraintInterface {

  async validate(value: string): Promise<boolean> {
    const exists = await database.findUser(value);
    return !exists;
  }

  defaultMessage(): string {
    return 'Значение должно быть уникальным';
  }
}

Асинхронный режим требует установки async: true, иначе библиотека будет интерпретировать результат как синхронный boolean.


Контекст выполнения и ограничения

ValidatorConstraintInterface предполагает отсутствие состояния в валидаторе, однако технически класс может содержать поля. Это допустимо, но может привести к проблемам при повторном использовании экземпляров.

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

  • валидатор должен быть детерминированным
  • результат validate не должен зависеть от внешнего изменяемого состояния без необходимости
  • асинхронные операции должны корректно обрабатываться через Promise

Интеграция с Dependency Injection

При использовании NestJS или других DI-контейнеров валидаторы могут быть зарегистрированы как провайдеры. В этом случае ValidatorConstraintInterface становится частью управляемого жизненного цикла объекта.

Это позволяет:

  • внедрять сервисы в валидатор
  • использовать репозитории и API-клиенты
  • централизованно управлять зависимостями

Типизация и строгая контрактность

TypeScript-реализация интерфейса обеспечивает строгую проверку соответствия:

  • отсутствие метода validate приведёт к ошибке компиляции
  • неправильный тип возвращаемого значения также фиксируется на этапе сборки

Это делает ValidatorConstraintInterface ключевым элементом типобезопасности в пользовательских расширениях Class-validator.


Поведение при ошибках выполнения

Если внутри validate возникает исключение, Class-validator интерпретирует это как неуспешную валидацию. Однако рекомендуется избегать исключений как механизма управления логикой и всегда возвращать boolean или Promise.


Расширенные сценарии применения

ValidatorConstraintInterface позволяет реализовывать:

  • кросс-полевую валидацию (сравнение нескольких свойств объекта)
  • проверку бизнес-правил (например, ограничения по возрасту и статусу)
  • интеграцию с внешними сервисами (проверка email, телефонов, идентификаторов)
  • динамическую валидацию на основе контекста запроса

Пример кросс-полевой проверки:

validate(value: any, args: ValidationArguments): boolean {
  const object = args.object as any;
  return value > object.minValue;
}

Взаимодействие с системой метаданных

Class-validator использует Reflect Metadata для хранения информации о декораторах. Реализация ValidatorConstraintInterface регистрируется в этой системе, что позволяет библиотеке:

  • находить все constraints
  • связывать их с полями классов
  • выполнять последовательную проверку объектов

Жизненный цикл constraint

Типичный цикл работы валидатора:

  1. Регистрация класса через @ValidatorConstraint
  2. Привязка через декоратор Validate
  3. Инициализация при валидации объекта
  4. Вызов validate для каждого значения
  5. При необходимости вызов defaultMessage
  6. Агрегация ошибок в общий результат

Производственные особенности

При проектировании валидаторов на основе ValidatorConstraintInterface учитываются следующие аспекты:

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

Эти факторы напрямую влияют на масштабируемость системы валидации в крупных приложениях.