diff --git a/src/container.ts b/src/container.ts index 2a288bde81..1d793b029a 100644 --- a/src/container.ts +++ b/src/container.ts @@ -31,7 +31,7 @@ const defaultContainer: { get(someClass: { new (...args: any[]): T } | Functi })(); let userContainer: { get(someClass: { new (...args: any[]): T } | Function): T }; -let userContainerOptions: UseContainerOptions; +let userContainerOptions: UseContainerOptions | undefined; /** * Sets container to be used by this library. diff --git a/src/decorator/common/Validate.ts b/src/decorator/common/Validate.ts index 59c80cdd08..a1110cad3c 100644 --- a/src/decorator/common/Validate.ts +++ b/src/decorator/common/Validate.ts @@ -38,11 +38,11 @@ export function Validate( constraintsOrValidationOptions?: any[] | ValidationOptions, maybeValidationOptions?: ValidationOptions ): PropertyDecorator { - return function (object: object, propertyName: string): void { + return function (object: object, propertyName: string | symbol): void { const args: ValidationMetadataArgs = { type: ValidationTypes.CUSTOM_VALIDATION, target: object.constructor, - propertyName: propertyName, + propertyName: propertyName as string, constraintCls: constraintClass, constraints: Array.isArray(constraintsOrValidationOptions) ? constraintsOrValidationOptions : undefined, validationOptions: !Array.isArray(constraintsOrValidationOptions) diff --git a/src/decorator/common/ValidateBy.ts b/src/decorator/common/ValidateBy.ts index 641cb00a06..22d7e89aa8 100644 --- a/src/decorator/common/ValidateBy.ts +++ b/src/decorator/common/ValidateBy.ts @@ -20,12 +20,15 @@ export function buildMessage( }; } +/** + * Validate properties + */ export function ValidateBy(options: ValidateByOptions, validationOptions?: ValidationOptions): PropertyDecorator { - return function (object: object, propertyName: string): void { + return function (object: object, propertyName: string | symbol): void { registerDecorator({ name: options.name, target: object.constructor, - propertyName: propertyName, + propertyName: propertyName as string, options: validationOptions, constraints: options.constraints, validator: options.validator, diff --git a/src/metadata/ConstraintMetadata.ts b/src/metadata/ConstraintMetadata.ts index 61ffcecc13..455bc6ac1b 100644 --- a/src/metadata/ConstraintMetadata.ts +++ b/src/metadata/ConstraintMetadata.ts @@ -30,7 +30,7 @@ export class ConstraintMetadata { constructor(target: Function, name?: string, async: boolean = false) { this.target = target; - this.name = name; + this.name = name || ''; this.async = async; } diff --git a/src/metadata/MetadataStorage.ts b/src/metadata/MetadataStorage.ts index 286346ac90..1439342274 100644 --- a/src/metadata/MetadataStorage.ts +++ b/src/metadata/MetadataStorage.ts @@ -63,8 +63,15 @@ export class MetadataStorage { groupByPropertyName(metadata: ValidationMetadata[]): { [propertyName: string]: ValidationMetadata[] } { const grouped: { [propertyName: string]: ValidationMetadata[] } = {}; metadata.forEach(metadata => { - if (!grouped[metadata.propertyName]) grouped[metadata.propertyName] = []; - grouped[metadata.propertyName].push(metadata); + if (!metadata.propertyName) return; + + const propertyName = metadata.propertyName.toString(); + + if (!grouped[propertyName]) { + grouped[propertyName] = []; + } + + grouped[propertyName].push(metadata); }); return grouped; } @@ -74,7 +81,7 @@ export class MetadataStorage { */ getTargetValidationMetadatas( targetConstructor: Function, - targetSchema: string, + targetSchema: string | undefined, always: boolean, strictGroups: boolean, groups?: string[] @@ -151,7 +158,7 @@ export class MetadataStorage { /** * Gets all validator constraints for the given object. */ - getTargetValidatorConstraints(target: Function): ConstraintMetadata[] { + getTargetValidatorConstraints(target?: Function): ConstraintMetadata[] { return this.constraintMetadatas.get(target) || []; } } diff --git a/src/metadata/ValidationMetadata.ts b/src/metadata/ValidationMetadata.ts index c1b1acce82..e8ebbde39b 100644 --- a/src/metadata/ValidationMetadata.ts +++ b/src/metadata/ValidationMetadata.ts @@ -32,17 +32,17 @@ export class ValidationMetadata { /** * Constraint class that performs validation. Used only for custom validations. */ - constraintCls: Function; + constraintCls?: Function; /** * Array of constraints of this validation. */ - constraints: any[]; + constraints?: any[]; /** * Validation message to be shown in the case of error. */ - message: string | ((args: ValidationArguments) => string); + message?: string | ((args: ValidationArguments) => string); /** * Validation groups used for this validation. @@ -83,9 +83,9 @@ export class ValidationMetadata { this.validationTypeOptions = args.validationTypeOptions; if (args.validationOptions) { this.message = args.validationOptions.message; - this.groups = args.validationOptions.groups; + this.groups = args.validationOptions.groups || []; this.always = args.validationOptions.always; - this.each = args.validationOptions.each; + this.each = args.validationOptions.each ?? false; this.context = args.validationOptions.context; } } diff --git a/src/validation/ValidationExecutor.ts b/src/validation/ValidationExecutor.ts index 98d09fce27..2880293853 100644 --- a/src/validation/ValidationExecutor.ts +++ b/src/validation/ValidationExecutor.ts @@ -36,7 +36,7 @@ export class ValidationExecutor { // Public Methods // ------------------------------------------------------------------------- - execute(object: object, targetSchema: string, validationErrors: ValidationError[]): void { + execute(object: object, targetSchema: string | undefined, validationErrors: ValidationError[]): void { /** * If there is no metadata registered it means possibly the dependencies are not flatterned and * more than one instance is used. @@ -151,8 +151,8 @@ export class ValidationExecutor { error.children = this.stripEmptyErrors(error.children); } - if (Object.keys(error.constraints).length === 0) { - if (error.children.length === 0) { + if (Object.keys(error.constraints || {}).length === 0) { + if (error.children?.length === 0) { return false; } else { delete error.constraints; @@ -244,7 +244,7 @@ export class ValidationExecutor { private conditionalValidations(object: object, value: any, metadatas: ValidationMetadata[]): ValidationMetadata[] { return metadatas - .map(metadata => metadata.constraints[0](object, value)) + .map(metadata => metadata.constraints?.[0](object, value)) .reduce((resultA, resultB) => resultA && resultB, true); } @@ -264,16 +264,23 @@ export class ValidationExecutor { property: metadata.propertyName, object: object, value: value, - constraints: metadata.constraints, + constraints: metadata.constraints || [], }; if (!metadata.each || !(Array.isArray(value) || value instanceof Set || value instanceof Map)) { const validatedValue = customConstraintMetadata.instance.validate(value, validationArguments); + if (isPromise(validatedValue)) { const promise = validatedValue.then(isValid => { if (!isValid) { const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + + if (!error.constraints) { + error.constraints = {}; + } + error.constraints[type] = message; + if (metadata.context) { if (!error.contexts) { error.contexts = {}; @@ -282,10 +289,16 @@ export class ValidationExecutor { } } }); + this.awaitingPromises.push(promise); } else { if (!validatedValue) { const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + + if (!error.constraints) { + error.constraints = {}; + } + error.constraints[type] = message; } } @@ -313,7 +326,12 @@ export class ValidationExecutor { const validationResult = flatValidatedValues.every((isValid: boolean) => isValid); if (!validationResult) { const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + + if (!error.constraints) { + error.constraints = {}; + } error.constraints[type] = message; + if (metadata.context) { if (!error.contexts) { error.contexts = {}; @@ -329,9 +347,13 @@ export class ValidationExecutor { return; } - const validationResult = validatedSubValues.every((isValid: boolean) => isValid); + const validationResult = validatedSubValues.every(isValid => isValid); if (!validationResult) { const [type, message] = this.createValidationError(object, value, metadata, customConstraintMetadata); + + if (!error.constraints) { + error.constraints = {}; + } error.constraints[type] = message; } }); @@ -358,13 +380,17 @@ export class ValidationExecutor { // Treats Set as an array - as index of Set value is value itself and it is common case to have Object as value const arrayLikeValue = value instanceof Set ? Array.from(value) : value; arrayLikeValue.forEach((subValue: any, index: any) => { - this.performValidations(value, subValue, index.toString(), [], metadatas, error.children); + this.performValidations(value, subValue, index.toString(), [], metadatas, error.children || []); }); } else if (value instanceof Object) { const targetSchema = typeof metadata.target === 'string' ? metadata.target : metadata.target.name; - this.execute(value, targetSchema, error.children); + this.execute(value, targetSchema, error.children || []); } else { const [type, message] = this.createValidationError(metadata.target as object, value, metadata); + + if (!error.constraints) { + error.constraints = {}; + } error.constraints[type] = message; } }); @@ -381,7 +407,7 @@ export class ValidationExecutor { const type = this.getConstraintType(metadata, customConstraint); - if (error.constraints[type]) { + if (error.constraints?.[type]) { if (!error.contexts) { error.contexts = {}; } @@ -405,7 +431,7 @@ export class ValidationExecutor { property: metadata.propertyName, object: object, value: value, - constraints: metadata.constraints, + constraints: metadata.constraints || [], }; let message = metadata.message || ''; diff --git a/src/validation/ValidationUtils.ts b/src/validation/ValidationUtils.ts index c67e8bcf1c..b56dac0fdc 100644 --- a/src/validation/ValidationUtils.ts +++ b/src/validation/ValidationUtils.ts @@ -20,7 +20,8 @@ export class ValidationUtils { message: string | ((args: ValidationArguments) => string), validationArguments: ValidationArguments ): string { - let messageString: string; + let messageString = ''; + if (message instanceof Function) { messageString = (message as (args: ValidationArguments) => string)(validationArguments); } else if (typeof message === 'string') {