Skip to content

Commit 88f0244

Browse files
committed
fix: overwrite validation decorators from parent class when defined in child
1 parent 221502c commit 88f0244

File tree

11 files changed

+207
-15
lines changed

11 files changed

+207
-15
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ export class Post {
371371

372372
## Inheriting Validation decorators
373373

374-
When you define a subclass which extends from another one, the subclass will automatically inherit the parent's decorators. If a property is redefined in the descendant, class decorators will be applied on it from both its own class and the base class.
374+
When you define a subclass which extends from another one, the subclass will automatically inherit the parent's decorators. If a property is redefined in the descendant, class decorators will be applied on it from both its own class and the base class. If a property-decorator pair is defined in both the subclass and parent-class, the decorator from the subclass will be used instead of the parent-class. For this purpose, "@IsDefined()" and "@IsOptional()" are considered the same.
375375

376376
```typescript
377377
import { validate } from 'class-validator';
@@ -733,6 +733,7 @@ Lets create another custom validation decorator called `IsUserAlreadyExist`:
733733
export function IsUserAlreadyExist(validationOptions?: ValidationOptions) {
734734
return function (object: Object, propertyName: string) {
735735
registerDecorator({
736+
name: 'isUserAlreadyExist',
736737
target: object.constructor,
737738
propertyName: propertyName,
738739
options: validationOptions,

src/decorator/common/Allow.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import { ValidationTypes } from '../../validation/ValidationTypes';
44
import { ValidationMetadata } from '../../metadata/ValidationMetadata';
55
import { getMetadataStorage } from '../../metadata/MetadataStorage';
66

7+
export const ALLOW = 'allow';
8+
79
/**
810
* If object has both allowed and not allowed properties a validation error will be thrown.
911
*/
1012
export function Allow(validationOptions?: ValidationOptions): PropertyDecorator {
1113
return function (object: object, propertyName: string): void {
1214
const args: ValidationMetadataArgs = {
15+
name: ALLOW,
1316
type: ValidationTypes.WHITELIST,
1417
target: object.constructor,
1518
propertyName: propertyName,

src/decorator/common/Validate.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { getMetadataStorage } from '../../metadata/MetadataStorage';
55
import { ValidationTypes } from '../../validation/ValidationTypes';
66
import { ConstraintMetadata } from '../../metadata/ConstraintMetadata';
77

8+
export const VALIDATE = 'validate';
9+
810
/**
911
* Registers custom validator class.
1012
*/
@@ -40,6 +42,7 @@ export function Validate(
4042
): PropertyDecorator {
4143
return function (object: object, propertyName: string): void {
4244
const args: ValidationMetadataArgs = {
45+
name: VALIDATE,
4346
type: ValidationTypes.CUSTOM_VALIDATION,
4447
target: object.constructor,
4548
propertyName: propertyName,

src/decorator/common/ValidateIf.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { ValidationTypes } from '../../validation/ValidationTypes';
44
import { ValidationMetadata } from '../../metadata/ValidationMetadata';
55
import { getMetadataStorage } from '../../metadata/MetadataStorage';
66

7+
const VALIDATE_IF = 'validateIf';
8+
79
/**
810
* Ignores the other validators on a property when the provided condition function returns false.
911
*/
@@ -13,6 +15,7 @@ export function ValidateIf(
1315
): PropertyDecorator {
1416
return function (object: object, propertyName: string): void {
1517
const args: ValidationMetadataArgs = {
18+
name: VALIDATE_IF,
1619
type: ValidationTypes.CONDITIONAL_VALIDATION,
1720
target: object.constructor,
1821
propertyName: propertyName,

src/decorator/common/ValidateNested.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ValidationTypes } from '../../validation/ValidationTypes';
44
import { ValidationMetadata } from '../../metadata/ValidationMetadata';
55
import { getMetadataStorage } from '../../metadata/MetadataStorage';
66

7+
const VALIDATE_NESTED = 'validateNested';
78
/**
89
* Objects / object arrays marked with this decorator will also be validated.
910
*/
@@ -14,6 +15,7 @@ export function ValidateNested(validationOptions?: ValidationOptions): PropertyD
1415

1516
return function (object: object, propertyName: string): void {
1617
const args: ValidationMetadataArgs = {
18+
name: VALIDATE_NESTED,
1719
type: ValidationTypes.NESTED_VALIDATION,
1820
target: object.constructor,
1921
propertyName: propertyName,

src/decorator/common/ValidatePromise.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import { ValidationTypes } from '../../validation/ValidationTypes';
44
import { ValidationMetadata } from '../../metadata/ValidationMetadata';
55
import { getMetadataStorage } from '../../metadata/MetadataStorage';
66

7+
export const VALIDATE_PROMISE = 'validatePromise';
8+
79
/**
810
* Resolve promise before validation
911
*/
1012
export function ValidatePromise(validationOptions?: ValidationOptions): PropertyDecorator {
1113
return function (object: object, propertyName: string): void {
1214
const args: ValidationMetadataArgs = {
15+
name: VALIDATE_PROMISE,
1316
type: ValidationTypes.PROMISE_VALIDATION,
1417
target: object.constructor,
1518
propertyName: propertyName,

src/metadata/MetadataStorage.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { ConstraintMetadata } from './ConstraintMetadata';
33
import { ValidationSchema } from '../validation-schema/ValidationSchema';
44
import { ValidationSchemaToMetadataTransformer } from '../validation-schema/ValidationSchemaToMetadataTransformer';
55
import { getGlobal } from '../utils';
6+
import { IS_DEFINED } from '../decorator/common/IsDefined';
7+
import { IS_OPTIONAL } from '../decorator/common/IsOptional';
68

79
/**
810
* Storage all metadatas.
@@ -138,10 +140,13 @@ export class MetadataStorage {
138140
// filter out duplicate metadatas, prefer original metadatas instead of inherited metadatas
139141
const uniqueInheritedMetadatas = inheritedMetadatas.filter(inheritedMetadata => {
140142
return !originalMetadatas.find(originalMetadata => {
141-
return (
142-
originalMetadata.propertyName === inheritedMetadata.propertyName &&
143-
originalMetadata.type === inheritedMetadata.type
144-
);
143+
const isSameProperty = originalMetadata.propertyName === inheritedMetadata.propertyName;
144+
const isSameValidator =
145+
originalMetadata.name === inheritedMetadata.name ||
146+
(originalMetadata.name === IS_DEFINED && inheritedMetadata.name === IS_OPTIONAL) ||
147+
(originalMetadata.name === IS_OPTIONAL && inheritedMetadata.name === IS_DEFINED);
148+
149+
return isSameProperty && isSameValidator;
145150
});
146151
});
147152

src/metadata/ValidationMetadata.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ export class ValidationMetadata {
1010
// -------------------------------------------------------------------------
1111

1212
/**
13-
* Validation type.
13+
* Validation type. Should be one of the ValidationTypes values.
1414
*/
1515
type: string;
1616

1717
/**
18-
* Validator name.
18+
* Validation name. Used to uniquely identify this validator.
1919
*/
20-
name?: string;
20+
name: string;
2121

2222
/**
2323
* Target class to which this validation is applied.

src/metadata/ValidationMetadataArgs.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { ValidationOptions } from '../decorator/ValidationOptions';
55
*/
66
export interface ValidationMetadataArgs {
77
/**
8-
* Validation type.
8+
* Validation type. Should be one of the ValidationTypes values.
99
*/
1010
type: string;
1111

1212
/**
13-
* Validator name.
13+
* Validation name. Used to uniquely identify this validator.
1414
*/
15-
name?: string;
15+
name: string;
1616

1717
/**
1818
* Object that is used to be validated.

src/validation-schema/ValidationSchema.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ export interface ValidationSchema {
1717
*/
1818
[propertyName: string]: {
1919
/**
20-
* Validation type. Should be one of the ValidationTypes value.
20+
* Validation type. Should be one of the ValidationTypes values.
2121
*/
2222
type: string;
2323

2424
/**
25-
* Validator name.
25+
* Validation name. Used to uniquely identify this validator.
2626
*/
27-
name?: string;
27+
name: string;
2828

2929
/**
3030
* Constraints set by validation type.

0 commit comments

Comments
 (0)