Декоратор @Matches применяется для валидации строковых
значений на соответствие регулярному выражению и является одним из
базовых инструментов проверки форматов данных в моделях, описанных через
class-validator. Его основная задача — гарантировать, что входное
значение строго соответствует заданному шаблону, будь то формат email,
телефонного номера, идентификатора, кода или любого другого
структурированного текста.
@Matches используется для проверки строки с помощью
регулярного выражения JavaScript. В отличие от более высокоуровневых
валидаторов, таких как @IsEmail или @IsUUID,
данный декоратор предоставляет полный контроль над логикой проверки.
Базовая форма применения:
import { Matches } from "class-validator";
class UserDto {
@Matches(/^[a-zA-Z0-9]+$/, {
message: "username может содержать только латинские буквы и цифры",
})
username: string;
}
Регулярное выражение передаётся первым аргументом, а объект настроек — вторым.
Общий формат:
@Matches(pattern: RegExp, validationOptions?: ValidationOptions)
RegExp, задающий
правило проверкиОсновные поля validationOptions:
message — пользовательское сообщение об ошибкеgroups — группы валидацииeach — применяется к каждому элементу массива (при
необходимости)context — дополнительный контекстПри валидации class-validator вызывает метод test() у
регулярного выражения:
pattern.test(value)
Если результат false, проверка считается
проваленной.
Важно учитывать, что поведение может меняться при использовании флага
g (global), так как он влияет на состояние
lastIndex.
g@Matches(/^[a-z]+$/g)
Использование g может приводить к нестабильной валидации
из-за внутреннего состояния регулярного выражения. Рекомендуется
избегать глобального флага при проверках.
class CreateUserDto {
@Matches(/^[a-z0-9_]{3,16}$/, {
message: "username должен содержать 3–16 символов: a-z, 0-9, _",
})
username: string;
}
Регулярное выражение ограничивает длину и набор допустимых символов.
class ContactDto {
@Matches(/^\+?[0-9]{10,15}$/, {
message: "телефон должен содержать от 10 до 15 цифр и может начинаться с +",
})
phone: string;
}
Такой подход позволяет контролировать базовый формат номера без привязки к конкретной стране.
class VerifyDto {
@Matches(/^[0-9]{6}$/, {
message: "код должен состоять из 6 цифр",
})
code: string;
}
Фиксированная длина и строгий цифровой формат.
Регулярные выражения могут учитывать регистр:
@Matches(/^[A-Z]+$/)
uppercaseCode: string;
Для игнорирования регистра применяется флаг i:
@Matches(/^[a-z]+$/i)
name: string;
Однако следует учитывать, что комбинирование i с более
сложными шаблонами может влиять на читаемость и предсказуемость
проверки.
При необходимости проверять каждый элемент массива используется
параметр each:
class TagsDto {
@Matches(/^[a-z]+$/, { each: true })
tags: string[];
}
В этом случае каждое значение массива валидируется отдельно по одному и тому же шаблону.
Валидация может включать составные шаблоны.
class LinkDto {
@Matches(
/^(https?:\/\/)?([\w-]+\.)+[\w-]+(\/[\w\-._~:/?#[\]@!$&'()*+,;=]*)?$/,
{
message: "некорректный URL",
}
)
url: string;
}
Подобные выражения часто применяются для базовой фильтрации, но не заменяют специализированные валидаторы.
Если значение не проходит проверку, class-validator возвращает объект ошибки:
{
"property": "username",
"constraints": {
"matches": "username должен содержать 3–16 символов: a-z, 0-9, _"
}
}
Поле constraints.matches формируется автоматически либо
переопределяется через message.
Сложные выражения снижают поддерживаемость кода. При увеличении логики проверки целесообразно выделять регулярку в отдельную константу:
const USERNAME_REGEX = /^[a-z0-9_]{3,16}$/;
class UserDto {
@Matches(USERNAME_REGEX)
username: string;
}
Регулярные выражения с большим количеством альтернатив и вложенных групп могут влиять на производительность при массовой валидации.
@Matches проверяет только форму строки, но не её смысл.
Например, строка может соответствовать шаблону email-подобного
выражения, но не быть реальным email.
Часто используется совместно с базовыми проверками типа:
class ProductDto {
@IsString()
@Matches(/^[A-Z0-9-]+$/)
sku: string;
}
Такое сочетание обеспечивает как типовую проверку, так и структурное ограничение.
В слоях DTO регулярные выражения позволяют фиксировать контракт данных:
class CategoryDto {
@Matches(/^[a-z0-9-]+$/)
slug: string;
}
Slug-формат обеспечивает единообразие URL-адресов и маршрутов.
Отсутствие ^ и $ приводит к частичным
совпадениям:
@Matches(/[a-z]+/)
Такая запись допускает лишние символы, если строка содержит подходящий фрагмент внутри.
Регулярные выражения, реализующие полноценную бизнес-логику, усложняют сопровождение и тестирование.
В строках JavaScript требуется двойное экранирование:
@Matches(/^\\d+$/)
в то время как в литерале RegExp:
@Matches(/^\d+$/)
Для крупных проектов регулярные выражения выносятся в отдельные модули:
export const REGEX = {
USERNAME: /^[a-z0-9_]{3,16}$/,
PHONE: /^\+?[0-9]{10,15}$/,
};
И используются централизованно во всех DTO.
@Matches применяется только к строкам. При передаче
значения другого типа (например, числа) поведение зависит от
предварительной трансформации class-transformer. Без
преобразования возможны некорректные результаты или пропуск
валидации.
При использовании class-transformer важно учитывать
порядок обработки:
class-validator)Если значение не приведено к строке, регулярное выражение может работать некорректно.
class PaymentDto {
@Matches(/^[0-9]{16}$/, { groups: ["card"] })
cardNumber: string;
}
Позволяет применять разные правила в зависимости от сценария.
@Matches(/^[A-Z]+$/, {
context: { type: "uppercase-code" },
})
code: string;
Контекст используется при кастомной обработке ошибок.
@Matches занимает нижний уровень валидационной иерархии:
он не знает о бизнес-логике, но обеспечивает строгий синтаксический
контроль входных данных. Его сила заключается в универсальности и прямом
управлении форматом, что делает его незаменимым при построении строгих
контрактов API и систем ввода данных.