@IsLowercase, @IsUppercase

В библиотеке class-validator значительная часть проверки данных строится вокруг декораторов, работающих на уровне строковых значений. Среди таких инструментов особое место занимают @IsLowercase() и @IsUppercase(), предназначенные для строгой проверки регистра символов в строках.

Эти валидаторы используются в DTO-моделях и классах данных для контроля формата входных значений, особенно когда регистр является частью бизнес-логики: коды, идентификаторы, токены, региональные обозначения, пользовательские алиасы и нормализованные строки.


@IsLowercase: проверка на строчные символы

Декоратор @IsLowercase() гарантирует, что строка содержит только символы в нижнем регистре. Любой символ верхнего регистра приводит к провалу валидации.

Базовое поведение

Проверка ориентируется на Unicode-строки и использует встроенные механизмы сравнения регистра, а не только ASCII-диапазон.

import { IsLowercase } from 'class-validator';

export class CreateUserDto {
  @IsLowercase()
  username: string;
}

В этом случае допустимыми значениями будут:

  • john
  • user123
  • api_key

Недопустимые значения:

  • John
  • USER
  • testValue

Особенности проверки регистра

1. Строгая проверка символов

@IsLowercase() не преобразует строку, а только проверяет её содержимое. Это важный принцип: библиотека не модифицирует данные.

// НЕ происходит преобразования:
value = value.toLowerCase(); // такого поведения нет

2. Игнорирование неалфавитных символов

Цифры, символы пунктуации и спецсимволы не влияют на результат проверки:

  • user_123 — валидно
  • api-key — валидно
  • token.v1 — валидно

Проверка касается только буквенных символов.


3. Unicode-символы

Валидация учитывает Unicode, поэтому работает не только с латиницей:

  • москва — валидно
  • berlin — валидно
  • 東京 — валидно (в языках без регистра проверка фактически нейтральна)

Важно учитывать, что в языках без различия регистра результат всегда будет успешным, поскольку верхнего/нижнего регистра не существует как категории.


@IsUppercase: проверка на заглавные символы

Декоратор @IsUppercase() выполняет обратную задачу — гарантирует, что все буквенные символы строки находятся в верхнем регистре.

Базовое использование

import { IsUppercase } from 'class-validator';

export class ApiKeyDto {
  @IsUppercase()
  apiKey: string;
}

Примеры допустимых значений:

  • ABC
  • USER_123
  • TOKEN-V1

Недопустимые значения:

  • Abc
  • token
  • ApiKEY

Логика работы с неалфавитными символами

Как и в случае с @IsLowercase(), цифры и спецсимволы игнорируются:

  • JWT_TOKEN_2024 — валидно
  • API-V2-KEY — валидно
  • SERVER_1 — валидно

Проверка затрагивает только буквы.


Поведение с Unicode и международными алфавитами

Регистрозависимые системы письма (латиница, кириллица, греческий алфавит) поддерживаются полностью:

  • МОСКВА — валидно
  • PARIS — валидно
  • ΑΘΗΝΑ — валидно

Однако в языках без регистра (китайский, японский, корейский слоговый/логографический набор) результат всегда будет успешным, так как сравнение регистра неприменимо.


Практическое применение в DTO-моделях

Нормализация входных данных

Оба декоратора часто применяются для обеспечения предсказуемого формата данных на уровне API.

import { IsUppercase, IsLowercase, Length } from 'class-validator';

export class RegisterDto {
  @IsLowercase()
  username: string;

  @IsUppercase()
  countryCode: string;

  @Length(8, 32)
  password: string;
}

Здесь реализуется строгая схема:

  • username всегда приводится к нижнему регистру до сохранения или уже приходит в нормализованном виде
  • countryCode фиксируется в стандарте ISO (например, US, DE, KZ)
  • password не зависит от регистра, но проверяется по длине

Поведение при пустых значениях и типах

Пустые строки

  • "" — считается валидной строкой для обоих декораторов, если не применены дополнительные ограничения (@IsNotEmpty())

null и undefined

По умолчанию оба декоратора не валидируют null и undefined, если не включены дополнительные ограничения:

import { IsOptional, IsUppercase } from 'class-validator';

class ExampleDto {
  @IsOptional()
  @IsUppercase()
  code?: string;
}

Взаимодействие с другими валидаторами

Комбинация с длиной строки

import { IsLowercase, Length } from 'class-validator';

class UserDto {
  @IsLowercase()
  @Length(3, 20)
  nickname: string;
}

Здесь порядок декораторов не влияет на результат, так как каждый выполняется независимо.


Комбинация с регулярными выражениями

import { Matches, IsUppercase } from 'class-validator';

class TokenDto {
  @IsUppercase()
  @Matches(/^[A-Z0-9-]+$/)
  token: string;
}

Такое сочетание часто избыточно, но применяется, когда требуется усиленный контроль формата.


Ошибки и сообщения валидации

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

  • is lowercase
  • is uppercase

Эти сообщения могут быть переопределены:

import { IsUppercase } from 'class-validator';

class Dto {
  @IsUppercase({ message: 'Код должен быть в верхнем регистре' })
  code: string;
}

Поведение в пайплайнах трансформации данных

В системах, где входные данные проходят через трансформацию (например, через class-transformer), важно понимать, что:

  • @IsLowercase() и @IsUppercase() не выполняют преобразование
  • они только проверяют результат уже полученного значения

Это означает, что любые изменения регистра должны происходить отдельно от валидации.


Распространённые ошибки при использовании

1. Ожидание автоматического преобразования

@IsUppercase()
name: string;

Ожидание: "test" станет "TEST"

Факт: значение не изменяется, только проверяется


2. Использование для сложных строковых форматов

@IsUppercase() не подходит для структурированных данных вроде JSON или email:

  • USER@MAIL.COM — технически валидно по регистру, но не является валидным email без дополнительной проверки
  • {TOKEN} — формально проходит проверку регистра, но не является полезным идентификатором

3. Игнорирование бизнес-логики

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

  • коды стран → @IsUppercase()
  • логины → @IsLowercase()
  • пароли → обычно без ограничения регистра

Влияние на архитектуру валидации данных

Использование @IsLowercase() и @IsUppercase() часто является частью стратегии нормализации данных на уровне входных DTO. Это позволяет:

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

В больших системах такие проверки становятся частью контракта API, а не просто вспомогательной валидацией.


Производительность и особенности реализации

Проверка регистра в class-validator выполняется синхронно и имеет минимальную стоимость:

  • одна проходка по строке
  • сравнение символов через Unicode-правила
  • отсутствие внешних зависимостей

На практике это делает декораторы подходящими для массовой валидации больших объектов без заметного влияния на производительность.


Поведение в цепочках валидации массивов и вложенных объектов

import { ValidateNested, IsUppercase } from 'class-validator';

class ItemDto {
  @IsUppercase()
  code: string;
}

class ContainerDto {
  @ValidateNested({ each: true })
  items: ItemDto[];
}

В этом случае каждый элемент массива проходит независимую проверку, включая регистровые ограничения.


Семантическая роль в архитектуре данных

@IsLowercase() и @IsUppercase() часто выступают не просто как валидаторы, а как декларативные ограничения модели данных. Они фиксируют соглашения:

  • формат идентификаторов
  • стандарты кодирования
  • правила совместимости между сервисами

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