Библиотека class-validator основана на механизме
декораторов TypeScript и метаданных, которые компилятор может
генерировать на этапе трансляции кода. Без корректной настройки
компилятора использование большинства возможностей библиотеки становится
невозможным или сильно ограниченным. Ключевую роль здесь играют два
параметра конфигурации TypeScript: experimentalDecorators и
emitDecoratorMetadata.
Декораторы — это специальный синтаксис, позволяющий модифицировать
классы, их свойства и методы. В контексте class-validator
они используются для объявления правил валидации прямо в модели
данных.
По умолчанию TypeScript не включает поддержку декораторов, поскольку они долгое время находились в стадии предложения (proposal stage) для JavaScript. Поэтому их необходимо явно активировать.
Для этого в файле tsconfig.json задаётся параметр:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
После включения этого флага компилятор начинает распознавать конструкции вида:
import { IsString } from "class-validator";
class User {
@IsString()
name: string;
}
Без experimentalDecorators такой код приведёт к ошибке
компиляции, так как символ @ будет считаться недопустимым
синтаксисом.
Одного включения декораторов недостаточно для полноценной работы
class-validator. Библиотека опирается на механизм рефлексии
типов, предоставляемый через пакет reflect-metadata. Для
того чтобы TypeScript генерировал информацию о типах свойств классов,
необходимо включить параметр:
{
"compilerOptions": {
"emitDecoratorMetadata": true
}
}
Этот флаг заставляет компилятор добавлять в сгенерированный JavaScript метаданные о типах параметров и свойств, которые затем могут быть прочитаны во время выполнения.
Метаданные, генерируемые TypeScript, сами по себе не доступны без
специального API. Для их чтения используется библиотека
reflect-metadata, которая полифилит предложение ECMAScript
Metadata Reflection API.
Её необходимо импортировать один раз на уровне приложения, обычно в точке входа:
import "reflect-metadata";
Без этого импорта декораторы могут применяться, но попытки получить
типы через class-validator завершатся отсутствием данных о
типах или ошибками валидации.
Корректная работа библиотеки требует согласованной конфигурации компилятора. Минимально необходимый набор опций выглядит следующим образом:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true
}
}
Каждый параметр играет свою роль:
reflect-metadata.При включённом emitDecoratorMetadata TypeScript
добавляет к классам специальные вызовы Reflect.metadata.
Например, следующий код:
class User {
name: string;
}
при компиляции превращается в нечто подобное:
__decorate([
Reflect.metadata("design:type", String)
], User.prototype, "name", void 0);
Эти данные затем используются class-validator и
class-transformer для определения типа свойства
name без необходимости явного указания.
TypeScript генерирует несколько ключевых типов метаданных:
В контексте class-validator наиболее важным является
design:type, так как он позволяет библиотеке понимать,
какие валидаторы применять по умолчанию или как интерпретировать
вложенные объекты.
Отсутствие корректной настройки приводит к ряду характерных проблем:
Класс выглядит корректно:
class User {
@IsString()
name: string;
}
но при вызове validate() ошибки не возвращаются. Причина
— отсутствуют метаданные типов.
При использовании вложенных DTO:
class Profile {
@IsString()
bio: string;
}
class User {
@ValidateNested()
profile: Profile;
}
без emitDecoratorMetadata библиотека не может определить
тип Profile, и валидация вложенных объектов не
выполняется.
Если не подключён reflect-metadata, возникает ошибка
выполнения:
ReferenceError: Reflect is not defined
или:
Reflect.getMetadata is not a function
Импорт reflect-metadata должен выполняться до
использования любых декораторов. Обычно он размещается в самом начале
входного файла:
import "reflect-metadata";
import { validate } from "class-validator";
Если импорт выполняется после объявления классов, метаданные могут не быть зарегистрированы корректно.
Использование разных инструментов сборки может влиять на работу декораторов:
tsconfig.json, иначе runtime не создаёт метаданные@babel/plugin-proposal-decorators и
@babel/plugin-proposal-class-properties, иначе поведение
отличается от TypeScriptОсобенно важно учитывать, что Babel по умолчанию не генерирует
emitDecoratorMetadata, что делает его несовместимым с
class-validator без дополнительных плагинов.
TypeScript поддерживает два режима декораторов:
experimentalDecorators: true)class-validator до сих пор ориентируется на
legacy-реализацию, поскольку именно она стабильно поддерживает генерацию
reflect-metadata. При использовании нового стандарта
возможны несовместимости, особенно в части метаданных типов.
В крупных проектах часто возникает ситуация, когда разные пакеты используют разные tsconfig. Это приводит к тому, что:
class-validator становится
непредсказуемымДля устранения проблемы необходимо обеспечить единый базовый
tsconfig, от которого наследуются все пакеты, с
обязательным включением:
experimentalDecoratorsemitDecoratorMetadataПроверить, что метаданные действительно генерируются, можно через простой тест:
import "reflect-metadata";
class Test {
value: string;
}
const type = Reflect.getMetadata("design:type", Test.prototype, "value");
console.log(type);
Ожидаемый результат:
[String: String]
Если вывод undefined, значит
emitDecoratorMetadata не работает или код не проходит через
TypeScript-компилятор.
При включённом strict режиме TypeScript усиливает
проверку типов, что снижает количество ситуаций, когда runtime-тип не
совпадает с compile-time типом. Это особенно важно в связке с
class-validator, поскольку библиотека работает на стыке
статической и динамической типизации.
Например, при строгой настройке:
name: string;
TypeScript не позволит случайно присвоить number, а
значит валидатор будет работать с более предсказуемыми данными.
В экосистеме NestJS эти параметры включаются автоматически в шаблонах
проектов, поскольку фреймворк активно использует
class-validator для DTO-валидации. В таких проектах
наличие:
"experimentalDecorators": true,
"emitDecoratorMetadata": true
является обязательным условием корректной работы пайплайна валидации запросов.
Несмотря на полезность, механизм имеет ограничения:
Это означает, что class-validator не может полагаться на
интерфейсы TypeScript и требует именно классовую структуру данных.
experimentalDecorators отвечает за возможность описания
правил валидации декларативно через @decorator.
emitDecoratorMetadata обеспечивает передачу информации о
типах в runtime, позволяя библиотеке автоматически определять структуру
данных.
Совместная работа этих механизмов формирует основу, на которой
строится вся модель валидации class-validator в
TypeScript-приложениях.