ESLint plugin to ensure class-validator decorators match TypeScript type annotations
npm install eslint-plugin-class-validator-type-matchESLint plugin to ensure class-validator decorators match TypeScript type annotations and optional property syntax.
npm
``bash`
npm install --save-dev eslint-plugin-class-validator-type-match
yarn
`bash`
yarn add -D eslint-plugin-class-validator-type-match
`typescript
import classValidatorTypeMatch from 'eslint-plugin-class-validator-type-match';
export default [classValidatorTypeMatch.flatConfigs.recommended];
`
Or with manual rule configuration:
`typescript
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',
},
},
];
`
`javascript`
// .eslintrc.js
module.exports = {
extends: ['plugin:class-validator-type-match/recommended'],
};
Or with manual rule configuration:
`javascript`
// .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',
},
};
- 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)
Ensures class-validator decorators match TypeScript type annotations for primitive types.
Examples:
`typescript
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
}
`
Ensures @ValidateNested() decorator is correctly applied for complex types.
Examples:
`typescript
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
}
`
Ensures @Type(() => ClassName) decorator matches TypeScript type annotations.
Examples:
`typescript
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
}
`
Ensures definite assignment assertion (!) is used correctly with decorators.
Examples:
`typescript
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
}
`
Ensures @IsOptional() decorator usage matches TypeScript optional property syntax (?).
Examples:
`typescript
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()
}
`
Ensures DTO class names match their file naming convention.
File Naming Rules:
- Files structured like . (e.g., .body.dto.ts, .query.dto.ts, .param.dto.ts) should have class names ending with (e.g., BodyDto, QueryDto, ParamDto).dto.ts
- Files structured like should have class names ending with Dto
Examples:
`typescript
// 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;
}
`
- @IsString → string@IsNumber
- / @IsInt / @IsPositive / @IsNegative → number@Min
- / @Max → number@IsBoolean
- → boolean@IsArray
- / @ArrayMinSize / @ArrayMaxSize → array or Array@IsDate
- / @MinDate / @MaxDate → Date@IsObject
- → object@IsEnum
- → enum
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`
-
MIT
Contributions are welcome! Please feel free to submit a Pull Request.