Skip to content

robertlinde/eslint-plugin-class-validator-type-match

Repository files navigation

eslint-plugin-class-validator-type-match

ESLint plugin to ensure class-validator decorators match TypeScript type annotations and optional property syntax.

Installation

npm

npm install --save-dev eslint-plugin-class-validator-type-match

yarn

yarn add -D eslint-plugin-class-validator-type-match

Usage

Flat Config (eslint.config.ts)

import classValidatorTypeMatch from 'eslint-plugin-class-validator-type-match';

export default [classValidatorTypeMatch.flatConfigs.recommended];

Or with manual rule configuration:

import classValidatorTypeMatch from 'eslint-plugin-class-validator-type-match';

export default [
  {
    plugins: {
      'class-validator-type-match': classValidatorTypeMatch,
    },
    rules: {
      'class-validator-type-match/decorator-type-match': 'error',
      'class-validator-type-match/optional-decorator-match': 'error',
      'class-validator-type-match/validate-nested-match': 'error',
      'class-validator-type-match/type-decorator-match': 'error',
      'class-validator-type-match/definite-assignment-match': 'error',
      'class-validator-type-match/dto-filename-match': 'error',
    },
  },
];

Legacy Config (.eslintrc)

// .eslintrc.js
module.exports = {
  extends: ['plugin:class-validator-type-match/recommended'],
};

Or with manual rule configuration:

// .eslintrc.js
module.exports = {
  plugins: ['class-validator-type-match'],
  rules: {
    'class-validator-type-match/decorator-type-match': 'error',
    'class-validator-type-match/optional-decorator-match': 'error',
    'class-validator-type-match/validate-nested-match': 'error',
    'class-validator-type-match/type-decorator-match': 'error',
    'class-validator-type-match/definite-assignment-match': 'error',
    'class-validator-type-match/dto-filename-match': 'error',
  },
};

Configuration Presets

  • recommended - All rules enabled (best for most projects)
  • strict - All rules enabled with strict settings
  • basic - Only core type matching rules (decorator-type-match, optional-decorator-match, definite-assignment-match, dto-filename-match)

Rules

decorator-type-match

Ensures class-validator decorators match TypeScript type annotations for primitive types.

Examples:

import {IsString, IsNumber, IsBoolean} from 'class-validator';

class User {
  @IsString()
  name!: number; // ❌ Error: Decorator @IsString does not match type annotation number

  @IsNumber()
  age!: string; // ❌ Error: Decorator @IsNumber does not match type annotation string

  @IsString()
  email!: string; // ✅ Correct

  @IsBoolean()
  isActive!: boolean; // ✅ Correct

  @IsString({each: true})
  tags!: string[]; // ✅ Correct - validates array elements

  @IsString({each: true})
  username!: string; // ❌ Error: { each: true } on non-array type
}

validate-nested-match

Ensures @ValidateNested() decorator is correctly applied for complex types.

Examples:

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

class User {
  @ValidateNested()
  address!: Address; // ✅ Correct - complex type with @ValidateNested

  @ValidateNested({each: true})
  addresses!: Address[]; // ✅ Correct - array of complex types with { each: true }

  @IsString()
  profile!: Profile; // ❌ Error: Complex type requires @ValidateNested()

  @ValidateNested()
  tags!: string[]; // ❌ Error: Primitive array doesn't need @ValidateNested

  @ValidateNested()
  history!: Event[]; // ❌ Error: Missing { each: true } for array of complex types
}

type-decorator-match

Ensures @Type(() => ClassName) decorator matches TypeScript type annotations.

Examples:

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

class User {
  @Type(() => Address)
  @ValidateNested()
  address!: Address; // ✅ Correct - @Type matches type

  @Type(() => Profile)
  @ValidateNested()
  address!: Address; // ❌ Error: @Type(() => Profile) doesn't match Address

  @ValidateNested()
  contact!: Contact; // ❌ Error: Missing @Type(() => Contact)

  @Type(() => Item)
  @ValidateNested({each: true})
  items!: Item[]; // ✅ Correct - @Type matches array element type
}

definite-assignment-match

Ensures definite assignment assertion (!) is used correctly with decorators.

Examples:

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

class User {
  @IsString()
  name!: string; // ✅ Correct - ! can be used for non-optional

  @IsString()
  email: string; // ✅ Correct - ! is optional for non-optional properties

  @IsOptional()
  @IsString()
  bio?: string; // ✅ Correct - optional property without !

  @IsOptional()
  @IsString()
  username?!: string; // ❌ Error: ! should not be used with optional (?)

  @IsString()
  nickname!: string = 'default'; // ❌ Error: ! should not be used with initializer

  @IsString()
  description!: string | undefined; // ❌ Error: ! should not be used with undefined type
}

optional-decorator-match

Ensures @IsOptional() decorator usage matches TypeScript optional property syntax (?).

Examples:

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

class User {
  @IsOptional()
  @IsString()
  name?: string; // ✅ Correct - decorator and syntax match

  @IsOptional()
  @IsString()
  email: string; // ❌ Error: Has @IsOptional() but property is not optional

  @IsOptional()
  @IsString()
  phone!: string; // ❌ Error: @IsOptional() conflicts with definite assignment

  @IsString()
  username?: string; // ❌ Error: Property is optional but missing @IsOptional()
}

dto-filename-match

Ensures DTO class names match their file naming convention.

File Naming Rules:

  • Files structured like .<type>.dto.ts (e.g., .body.dto.ts, .query.dto.ts, .param.dto.ts) should have class names ending with <Type>Dto (e.g., BodyDto, QueryDto, ParamDto)
  • Files structured like .dto.ts should have class names ending with Dto

Examples:

// File: create-user.body.dto.ts
import {IsString, IsEmail} from 'class-validator';

export class CreateUserBodyDto {
  // ✅ Correct - ends with BodyDto
  @IsString()
  name!: string;

  @IsEmail()
  email!: string;
}

// File: create-user.body.dto.ts
export class CreateUserDto {
  // ❌ Error: Expected class name to end with "BodyDto"
  @IsString()
  name!: string;
}

// File: update-profile.query.dto.ts
export class UpdateProfileQueryDto {
  // ✅ Correct - ends with QueryDto
  @IsString()
  filter?: string;
}

// File: user.dto.ts
export class UserDto {
  // ✅ Correct - ends with Dto
  @IsString()
  id!: string;
}

// File: user.dto.ts
export class User {
  // ❌ Error: Expected class name to end with "Dto"
  @IsString()
  id!: string;
}

// File: get-user.param.dto.ts
export class GetUserParamDto {
  // ✅ Correct - ends with ParamDto
  @IsString()
  userId!: string;
}

Supported Decorators

Type Validators

  • @IsStringstring
  • @IsNumber / @IsInt / @IsPositive / @IsNegativenumber
  • @Min / @Maxnumber
  • @IsBooleanboolean
  • @IsArray / @ArrayMinSize / @ArrayMaxSizearray or Array<T>
  • @IsDate / @MinDate / @MaxDateDate
  • @IsObjectobject
  • @IsEnumenum

Type-Agnostic Validators

The following decorators work with any type and are not checked by decorator-type-match:

  • @IsOptional
  • @ValidateNested
  • @IsDefined
  • @IsEmpty
  • @IsNotEmpty
  • @Equals
  • @NotEquals
  • @IsIn
  • @IsNotIn
  • @Type
  • @Transform

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

About

ESLint plugin to ensure class-validator decorators match TypeScript type annotations

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors