class-validator icon indicating copy to clipboard operation
class-validator copied to clipboard

Unable to use cutom validator, since it is saying it is not a function

Open NicolasGorga opened this issue 1 year ago • 3 comments

Hello, I defined a custom validator, as many others i have working correctly, but i am getting an error:

(0, is_valid_order_generation_flow_1.IsValidOrderGenerationFlow)(), ^ TypeError: (0 , is_valid_order_generation_flow_1.IsValidOrderGenerationFlow) is not a function at Object. (E:\proyectos personales\centr0\backend\universal-clothing-api\node_modules\universal-clothing-shared\dist\order\CreateOrderRequestDTO.js:27:69) at Module._compile (node:internal/modules/cjs/loader:1376:14) at Module._extensions..js (node:internal/modules/cjs/loader:1435:10) at Object.require.extensions. [as .js] (E:\proyectos personales\centr0\backend\universal-clothing-api\node_modules\ts-node\src\index.ts:1608:43) at Module.load (node:internal/modules/cjs/loader:1207:32) at Function.Module._load (node:internal/modules/cjs/loader:1023:12) at Module.require (node:internal/modules/cjs/loader:1235:19) at require (node:internal/modules/helpers:176:18) at Object. (E:\proyectos personales\centr0\backend\universal-clothing-api\node_modules\universal-clothing-shared\dist\order\index.js:18:14) at Module._compile (node:internal/modules/cjs/loader:1376:14)

I am using the validator in my express api, which imports it from an npm package i published where i expose types and validators to share between my frontend and backend. Here is how the validator looks in the shared types library:

`import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, registerDecorator } from "class-validator"; import { allowedOrderGenerationFlows } from "../../order";

@ValidatorConstraint({ name: 'isValidOrderGenerationFlow', async: false }) class IsValidOrderGenerationFlowConstraint implements ValidatorConstraintInterface { validate(value: any, args?: ValidationArguments | undefined) { return allowedOrderGenerationFlows.includes(value); }

defaultMessage?(validationArguments?: ValidationArguments | undefined): string {
    return  `Flow de generacion de orden: ${validationArguments?.value} es invalido. Valores posibles son ${allowedOrderGenerationFlows.join(', ')}`;
}

}

export function IsValidOrderGenerationFlow(validationOptions?: ValidationOptions) { return function(object: Object, propertyName: string) { registerDecorator({ target: object.constructor, propertyName, constraints: [], options: validationOptions, validator: IsValidOrderGenerationFlowConstraint, }); } }`

Here is the DTO in the shared library that uses it:

`import { ArrayNotEmpty, IsEmail, IsOptional, IsString } from "class-validator"; import { OrderGenerationFlow } from "."; import { IsValidOrderGenerationFlow } from "../typeorm/custom-validators/is-valid-order-generation-flow";

export class CreateOrderRequestDTO { @ArrayNotEmpty({ message: 'No puede ser un array vacio' }) @IsString({ message: 'Cada elemento debe ser un string', each: true }) items!: string[];

@IsValidOrderGenerationFlow()
orderGenerationFlow!: OrderGenerationFlow;

@IsOptional()
@IsEmail(undefined, { message: "Debe ser un email valido" })
clientEmail?: string;

}`

And here is how i am using it in my backend, just like all other DTOs i use which don't fail, even those using a custom validator:

const requestDTO = await validateRequestBody(OrderDTO.CreateOrderRequestDTO, req.body);

I have another custom validator which doesn't fail which is almost identical to this one, in terms of the validation it performs and the usage inside a DTO. This doesn't fail:

`import { ValidationArguments, ValidationOptions, ValidatorConstraint, ValidatorConstraintInterface, registerDecorator } from "class-validator"; import { allowedProductConditions } from "../../product";

@ValidatorConstraint({ name: 'isValidProductCondition', async: false }) class IsValidProductConditionConstraint implements ValidatorConstraintInterface { validate(condition: any, args: ValidationArguments) { return allowedProductConditions.includes(condition); }

defaultMessage?(validationArguments?: ValidationArguments | undefined): string {
    return  `Invalid product condition. Allowed product conditions: ${allowedProductConditions.join(', ')}`;
}

}

export function IsValidProductCondition(validationOptions?: ValidationOptions) { return function (object: Object, propertyName: string) { registerDecorator({ target: object.constructor, propertyName, options: validationOptions, constraints: [], validator: IsValidProductConditionConstraint, }); } }`

DTO usage:

` import { Type } from 'class-transformer'; import { ArrayNotEmpty, IsArray, IsBoolean, IsNotEmptyObject, IsNumber, IsOptional, IsPositive, IsString, MinLength, ValidateNested } from 'class-validator'; import { AssignSizeToProductDTO, BaseProductConditionDTO, ProductCondition, ProductSizeDTO } from '.'; import { BaseStoreDTO } from '../store'; import { IsValidProductCondition } from '../typeorm/custom-validators/is-valid-product-condition'; import { ProductBaseCategoryDTO } from './ProductCategory';

export class CreateProductDTO { @IsString() title!: string;

@IsPositive() 
price!: number;

@IsValidProductCondition()
condition!: ProductCondition;

@IsNotEmptyObject(undefined, { message: '\'size\' tiene que ser un objeto' })
@ValidateNested()
@Type(() => AssignSizeToProductDTO)
size!: AssignSizeToProductDTO

@IsNumber()
categoryId!: number;

@IsOptional()
@IsString()
description?: string;

@IsOptional()
@IsString()
brand?: string;

@IsOptional()
@IsString()
color?: string;

}`

Please this is driving me nuts, any help is appreciated!

NicolasGorga avatar Apr 10 '24 22:04 NicolasGorga

我也遇到了这个问题,在0.14.1新版本也依旧存在。

xhc-code avatar May 08 '25 09:05 xhc-code

This isn't a package problem, you have a circular dependency somewhere that prevents the validator from being registered before it is used.

gigagamerofdeath avatar May 12 '25 08:05 gigagamerofdeath

Image But you see that the type of constraintClass is Function, which is incorrect because the custom validator is an instance of a certain interface, not a function. https://github.com/typestack/class-validator/blob/develop/src/decorator/common/Validate.ts

Image Here you can see that CustomTextLength is a class rather than a Function type.

xhc-code avatar May 12 '25 08:05 xhc-code