class-validator для TypeScript: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в class-validator

class-validator — это мощная и популярная библиотека для валидации данных в TypeScript и JavaScript, построенная на основе декораторов. Она позволяет декларативно описывать правила валидации прямо внутри классов, используя аннотации. Это делает код чистым, читаемым и легко поддерживаемым.

Библиотека особенно полезна при разработке серверных приложений (например, на NestJS или Express), где необходимо проверять входящие запросы, данные из форм, конфигурации и любые другие структуры данных. Вместо того чтобы писать громоздкие цепочки if-else, вы просто добавляете декораторы к свойствам класса.

Зачем нужна class-validator?

  • Декларативность: Правила валидации определяются рядом с данными, что улучшает читаемость.
  • Мощный набор валидаторов: Включает десятки встроенных декораторов для проверки строк, чисел, дат, массивов, вложенных объектов и многого другого.
  • Кастомные валидаторы: Позволяет легко создавать свои собственные правила.
  • Интеграция с TypeScript: Полная поддержка типов и декораторов.
  • Асинхронная валидация: Поддерживает проверки, требующие асинхронных операций (например, проверка уникальности в БД).

Установка

Для использования class-validator вам потребуется Node.js и npm/yarn. Установите библиотеку и необходимые зависимости:

npm install class-validator class-transformer reflect-metadata

Или через yarn:

yarn add class-validator class-transformer reflect-metadata

Важно: Библиотека reflect-metadata обязательна для работы декораторов. Убедитесь, что она импортирована в вашем главном файле (например, index.ts или main.ts):

import 'reflect-metadata';

Также убедитесь, что в вашем tsconfig.json включены следующие настройки:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Быстрый старт

Давайте создадим простую модель пользователя и провалидируем её.

import 'reflect-metadata';
import { validate, IsString, IsEmail, MinLength } from 'class-validator';
import { plainToClass } from 'class-transformer';

class User {
    @IsString()
    @MinLength(3, { message: 'Имя должно содержать минимум 3 символа' })
    name: string;

    @IsEmail({}, { message: 'Некорректный email' })
    email: string;

    constructor(name: string, email: string) {
        this.name = name;
        this.email = email;
    }
}

async function main() {
    // Создаем объект с некорректными данными
    const userData = { name: 'Jo', email: 'not-an-email' };
    
    // Преобразуем простой объект в экземпляр класса
    const user = plainToClass(User, userData);
    
    // Валидируем
    const errors = await validate(user);
    
    if (errors.length > 0) {
        console.log('Ошибки валидации:', errors);
        errors.forEach(error => {
            console.log(`Поле: ${error.property}`);
            for (const constraint in error.constraints) {
                console.log(`  - ${error.constraints[constraint]}`);
            }
        });
    } else {
        console.log('Валидация пройдена успешно!');
    }
}

main();
// Вывод:
// Ошибки валидации: [...]
// Поле: name
//   - Имя должно содержать минимум 3 символа
// Поле: email
//   - Некорректный email

Основные методы и декораторы

1. @IsString()

Что делает: Проверяет, является ли значение строкой.

Параметры: Принимает опциональный объект с опциями валидации (например, { message: '...' }).

class Product {
    @IsString()
    title: string;
}

2. @IsNumber()

Что делает: Проверяет, является ли значение числом (включая числа с плавающей точкой).

Параметры: Принимает опции, включая allowNaN (допускать NaN) и allowInfinity.

class Order {
    @IsNumber({ allowNaN: false })
    price: number;
}

3. @IsEmail()

Что делает: Проверяет, является ли строка корректным email-адресом.

Параметры: Принимает опции для настройки проверки (например, allow_display_name).

class Contact {
    @IsEmail()
    email: string;
}

4. @MinLength() и @MaxLength()

Что делает: Проверяют минимальную и максимальную длину строки.

Параметры: Число — требуемая длина.

class Post {
    @MinLength(10, { message: 'Заголовок слишком короткий' })
    @MaxLength(100, { message: 'Заголовок слишком длинный' })
    title: string;
}

5. @Min() и @Max()

Что делает: Проверяют минимальное и максимальное числовое значение.

Параметры: Число — пороговое значение.

class Product {
    @Min(0)
    @Max(10000)
    price: number;
}

6. @IsArray()

Что делает: Проверяет, является ли значение массивом.

Параметры: Опциональные опции.

class Article {
    @IsArray()
    tags: string[];
}

7. @ArrayMinSize() и @ArrayMaxSize()

Что делает: Проверяют минимальный и максимальный размер массива.

Параметры: Число — требуемый размер.

class Team {
    @ArrayMinSize(2, { message: 'В команде должно быть минимум 2 участника' })
    @ArrayMaxSize(10)
    members: string[];
}

8. @IsBoolean()

Что делает: Проверяет, является ли значение булевым (true/false).

Параметры: Опциональные опции.

class Settings {
    @IsBoolean()
    isActive: boolean;
}

9. @IsOptional()

Что делает: Указывает, что свойство может быть пропущено (undefined) и не будет валидироваться, если его нет.

Параметры: Нет.

class Profile {
    @IsString()
    username: string;

    @IsOptional()
    @IsString()
    bio?: string;
}

10. @ValidateNested()

Что делает: Позволяет валидировать вложенные объекты. Обязательно использовать с @Type() из class-transformer.

Параметры: Опциональные опции.

import { Type } from 'class-transformer';

class Address {
    @IsString()
    street: string;

    @IsString()
    city: string;
}

class Person {
    @ValidateNested()
    @Type(() => Address)
    address: Address;
}

11. @IsEnum()

Что делает: Проверяет, является ли значение одним из значений перечисления (enum).

Параметры: Объект enum.

enum Role {
    ADMIN = 'admin',
    USER = 'user',
    GUEST = 'guest'
}

class User {
    @IsEnum(Role)
    role: Role;
}

12. @Matches()

Что делает: Проверяет, соответствует ли строка регулярному выражению.

Параметры: RegExp или строка с регулярным выражением.

class Code {
    @Matches(/^[A-Z]{3}-\d{4}$/, { message: 'Код должен быть формата ABC-1234' })
    productCode: string;
}

Реальный практический пример

Рассмотрим пример создания REST API для регистрации пользователя с использованием Express и class-validator.

import 'reflect-metadata';
import express, { Request, Response } from 'express';
import { validate, IsString, IsEmail, MinLength, IsOptional, IsDateString } from 'class-validator';
import { plainToClass } from 'class-transformer';

class CreateUserDto {
    @IsString()
    @MinLength(3)
    username: string;

    @IsEmail()
    email: string;

    @IsString()
    @MinLength(8, { message: 'Пароль должен быть минимум 8 символов' })
    password: string;

    @IsOptional()
    @IsDateString()
    birthDate?: string;
}

const app = express();
app.use(express.json());

app.post('/users', async (req: Request, res: Response) => {
    const userDto = plainToClass(CreateUserDto, req.body);
    const errors = await validate(userDto);

    if (errors.length > 0) {
        const formattedErrors = errors.map(error => ({
            field: error.property,
            messages: Object.values(error.constraints || {})
        }));
        return res.status(400).json({ errors: formattedErrors });
    }

    // Здесь можно сохранить пользователя в БД
    res.status(201).json({ message: 'Пользователь создан', data: userDto });
});

app.listen(3000, () => console.log('Server running on port 3000'));

Советы и типичные ошибки

Советы

  • Используйте class-transformer: Всегда преобразуйте обычные объекты в экземпляры классов с помощью plainToClass перед валидацией. Иначе декораторы не сработают.
  • Кастомизируйте сообщения об ошибках: Используйте параметр { message: 'Ваше сообщение' } для каждого декоратора, чтобы сделать ошибки понятными для пользователя.
  • Группируйте валидации: Используйте группы (groups) для разных сценариев (создание, обновление).
  • Создавайте кастомные валидаторы: Если встроенных недостаточно, используйте @ValidatorConstraint().

Типичные ошибки

  • Забыли импортировать reflect-metadata: Без этого декораторы не будут работать.
  • Не включили experimentalDecorators в tsconfig: TypeScript не поймет синтаксис декораторов.
  • Валидация без преобразования: Если вы передаете простой объект напрямую в validate(), валидация не сработает, так как декораторы не привязаны к свойствам объекта.
  • Игнорирование асинхронности: validate() возвращает Promise, не забывайте использовать await.

Ссылки и ресурсы

Рекомендаций

LightGBM – быстрый градиентный бустинг

LightGBM – быстрый градиентный бустинг

Стандартная библиотека Kotlin: полный обзор

Стандартная библиотека Kotlin: полный обзор

Создание CLI-приложений на Node.js: фреймворки Commander и Yargs

Создание CLI-приложений на Node.js: фреймворки Commander и Yargs

io-ts для TypeScript: полное руководство с примерами

io-ts для TypeScript: полное руководство с примерами