@IsArray

Декоратор @IsArray из библиотеки class-validator используется для проверки того, что значение свойства является массивом. Он относится к базовым валидаторам структуры данных и часто применяется в DTO-объектах, где требуется строгая типизация входных данных.


Назначение и поведение

Основная задача @IsArray — убедиться, что проверяемое значение имеет тип Array.

Проверка выполняется строго:

  • [] — валидно
  • [1, 2, 3] — валидно
  • ['a', 'b'] — валидно
  • "text" — невалидно
  • {} — невалидно
  • null — невалидно (если не указано nullable поведение через другие декораторы)

Важно понимать, что проверка не углубляется в содержимое массива. Она не анализирует тип элементов — только сам факт, что значение является массивом.


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

Типичный сценарий применения — DTO-классы в связке с class-validator и class-transformer.

import { IsArray } from 'class-validator';

export class CreateUserDto {
  @IsArray()
  roles: string[];
}

В этом примере свойство roles должно быть массивом. Любое другое значение приведёт к ошибке валидации.


Поведение без трансформации данных

В JavaScript и TypeScript данные, поступающие извне (например, из HTTP-запроса), часто приходят в виде строк.

Пример проблемного случая:

{
  "roles": "admin"
}

Без преобразования class-validator увидит строку и отклонит её, так как строка не является массивом.


Взаимодействие с class-transformer

@IsArray не выполняет преобразование типов. Для корректной работы с входными данными часто используется class-transformer.

import { Type } from 'class-transformer';
import { IsArray } from 'class-validator';

export class CreateUserDto {
  @IsArray()
  @Type(() => String)
  roles: string[];
}

Здесь:

  • @Type(() => String) пытается привести элементы к строкам
  • @IsArray() проверяет, что значение является массивом

Проверка массива с вложенными структурами

@IsArray не проверяет содержимое, поэтому для сложных структур требуется комбинирование с другими валидаторами.

import { IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';

class PermissionDto {
  name: string;
}

export class CreateRoleDto {
  @IsArray()
  @ValidateNested({ each: true })
  @Type(() => PermissionDto)
  permissions: PermissionDto[];
}

Здесь:

  • @IsArray() проверяет, что permissions — массив
  • @ValidateNested({ each: true }) валидирует каждый элемент массива
  • @Type() обеспечивает корректную трансформацию объектов

Параметр each в связке с другими декораторами

Хотя сам @IsArray не принимает параметр each, он часто используется вместе с другими декораторами, которые его поддерживают.

Пример:

import { IsArray, IsString } from 'class-validator';

export class CreateDto {
  @IsArray()
  @IsString({ each: true })
  tags: string[];
}

Здесь:

  • @IsArray() проверяет структуру
  • @IsString({ each: true }) проверяет каждый элемент массива

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

1. Отсутствие трансформации

Проблема:

roles: "admin,user"

Ожидание: массив Фактически: строка

Решение: использовать @Type(() => ...) или предварительно парсить данные.


2. Путаница с объектами

roles: { 0: 'admin', 1: 'user' }

Это объект, а не массив, даже если структура похожа.


3. Игнорирование вложенной валидации

@IsArray()
permissions: any[];

Без @ValidateNested содержимое массива остаётся невалидированным.


Поведение с пустыми массивами

Пустой массив считается валидным значением:

roles: []

@IsArray не требует наличия элементов.

Если необходимо запрещать пустые массивы, используется дополнительный декоратор:

import { ArrayNotEmpty, IsArray } from 'class-validator';

@IsArray()
@ArrayNotEmpty()
roles: string[];

Сочетание с другими валидаторами

@IsArray редко используется изолированно. На практике он комбинируется:

  • @ArrayMinSize, @ArrayMaxSize
  • @ArrayNotEmpty
  • @IsString({ each: true })
  • @ValidateNested({ each: true })

Пример комплексной валидации:

import {
  IsArray,
  ArrayMinSize,
  ArrayMaxSize,
  IsInt,
} from 'class-validator';

export class NumbersDto {
  @IsArray()
  @ArrayMinSize(2)
  @ArrayMaxSize(5)
  @IsInt({ each: true })
  values: number[];
}

Особенности работы в TypeScript

TypeScript-типизация не заменяет runtime-валидацию.

roles: string[];

Даже если тип указан как string[], во время выполнения значение может быть любым. Именно поэтому @IsArray критически важен в API-слое.


Проверка примитивных и сложных типов

@IsArray одинаково работает для любых массивов:

  • массив строк
  • массив чисел
  • массив объектов
  • массив смешанных типов

Однако смешанные типы усложняют последующую обработку и обычно считаются антипаттерном в DTO-структурах.


Производственные сценарии использования

REST API DTO

export class UpdateUserDto {
  @IsArray()
  roles: string[];

  @IsArray()
  permissions: string[];
}

GraphQL input типы

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

Конфигурационные объекты

export class AppConfig {
  @IsArray()
  allowedOrigins: string[];
}

Ограничения

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

Взаимодействие с null и undefined

@IsArray не пропускает null и undefined по умолчанию.

Для разрешения отсутствующих значений используется:

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

@IsOptional()
@IsArray()
roles: string[];

Поведение при наследовании DTO

При расширении классов валидация сохраняется:

class BaseDto {
  @IsArray()
  roles: string[];
}

class ExtendedDto extends BaseDto {
  additional: string;
}

Итоговая логика проверки

@IsArray выполняет единственную операцию:

  • проверка Array.isArray(value) === true

Все остальные аспекты — длина, содержимое, типы элементов — выносятся в дополнительные декораторы.