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

value is getting undefined in case of custom validation for file

Open nitinmagdumvista opened this issue 2 years ago • 6 comments

i want to validate the file type also if the file is provided or not i am writing custom validator but in both case with and without file its giving me value undefined

// ============ custom validator

`import { Injectable } from '@nestjs/common'; import { registerDecorator, ValidationArguments, ValidationOptions, ValidatorConstraint, } from '@nestjs/class-validator'; import { ValidatorConstraintInterface } from '@nestjs/class-validator/types/validation/ValidatorConstraintInterface';

// create Constraint @ValidatorConstraint({ async: true }) @Injectable() export class ValidateEnumContsraint1 implements ValidatorConstraintInterface { private error: string;

defaultMessage(validationArguments?: ValidationArguments): string { return this.error; }

validate(value: any, validationArguments?: ValidationArguments): Promise | boolean { try { console.log('value', value); console.log('validationArguments', validationArguments); return true; } catch (e) { this.error = Error in input value for ${validationArguments.property} - ${JSON.stringify(e)}; return false; } } }

// create decorator export function ValidateFileDecorator(enumName: any, validationOptions?: ValidationOptions) { return function (object: any, propertyName: string) { registerDecorator({ target: object.constructor, propertyName: propertyName, options: validationOptions, constraints: [enumName], validator: ValidateEnumContsraint1, }); }; } `

//=================== validatore used inside DTO

@ValidateFileRequiredDecorator() @ApiProperty({ type: 'string', format: 'binary', required: true }) readonly file: Express.Multer.File;

nitinmagdumvista avatar Jan 23 '23 09:01 nitinmagdumvista

+1 This problem...

"this" object all properties set to undefined when use new keyword.

OguzBey avatar Jan 25 '23 15:01 OguzBey

version: 0.14.0

example code:


export class ClassValidatorDTO {
  constructor(data: Record<string, any>) {
    Object.assign(this, { ...data });
  }

  checkErrors() {
    let errors = validateSync(this, { whitelist: true });
    return errors;
  }
}

export class UserCreateDTO extends ClassValidatorDTO {
  @Length(3, 20)
  username: string;
  @Length(8, 32)
  password: string;
  @IsEmail()
  email: string;
  @Length(3, 20)
  name: string;
  @Length(3, 20)
  surname: string;
}

let reqBody = new UserCreateDTO(data); // reqBody all props undefined now
console.dir({...reqBody}, {depth: null})
let errors = reqBody.checkErrors();
if (errors.length) console.log(errors)
console.dir({...reqBody})

OguzBey avatar Jan 25 '23 15:01 OguzBey

I think it's a problem with typescript compile settings. target

I just got this error in es2022.

OguzBey avatar Jan 25 '23 16:01 OguzBey

I can't reproduce whatever issue you seem to be describing here. Here's my attempt at reproducing your issue, and everything seems to work fine (your validator is just console.logging the field value, so there's really nothing that can go wrong there):

import { plainToInstance } from "class-transformer";
import {
  registerDecorator,
  validate,
  ValidationArguments,
  ValidationOptions,
  ValidatorConstraint,
  ValidatorConstraintInterface,
} from "class-validator";

// create Constraint
@ValidatorConstraint({ async: true })
export class ValidateEnumContsraint1 implements ValidatorConstraintInterface {
  private error: string;

  defaultMessage(validationArguments?: ValidationArguments): string {
    return this.error;
  }

  validate(
    value: any,
    validationArguments?: ValidationArguments
  ): Promise<any> | boolean {
    try {
      console.log("value", value);
      console.log("validationArguments", validationArguments);
      return true;
    } catch (e) {
      this.error = `Error in input value for ${
        validationArguments.property
      } - ${JSON.stringify(e)}`;

      return false;
    }
  }
}

// create decorator
export function ValidateFileDecorator(
  enumName: any,
  validationOptions?: ValidationOptions
) {
  return function (object: any, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: [enumName],
      validator: ValidateEnumContsraint1,
    });
  };
}

class MyClass {
  @ValidateFileDecorator("banana")
  file: unknown;
}

const plain = { file: "this is a file I guess" };

const instance = plainToInstance(MyClass, plain);

const errors = await validate(instance);
console.log(errors); // []

braaar avatar Jan 27 '23 12:01 braaar

I think it's a problem with typescript compile settings. target

I just got this error in es2022.

I face up with same problem. And you are right! Option "target": "es2022" in tsconfig is reason. I tried downgrade to "es2021" - and undefined is gone.

Steps to reproduce with some controller file:

import { Controller, Post, Body } from '@nestjs/common';
import { IsOptional } from 'class-validator';

export class TestDto {
  @IsOptional()
  option1: string;

  @IsOptional()
  option2: string;
}

@Controller('test')
export class TestController {
  @Post('/')
  createTest(@Body() body: TestDto) {
    /* Input body value is 
      {
         option1: 'val1'
      }
     */
    console.log('----------Log test dto');
    console.log(body);
    // tsconfig with "target": "es2022" result is: TestDto { option1: 'val1', option2: undefined }
    // tsconfig with "target": "es2021" result is: TestDto { option1: 'val1' }

    return body;
  }
}

Some package versions: nodejs v18.19.1 "class-validator": "^0.14.1", "@nestjs/common": "^9.3.12", "typescript": "~5.3.3"

Compiler options

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "allowSyntheticDefaultImports": true,
    "target": "es2022", // or es2021
    "sourceMap": true,

    "outDir": "./dist",
    "baseUrl": "./",

    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false,

    "incremental": true,
    "skipLibCheck": true,

    "strict": true
  },
  "exclude": [
    "node_modules",
  ]
}

ai-leonid avatar Apr 01 '24 20:04 ai-leonid

So reason is useDefineForClassFields option in tsconfig. For target ES2022 or higher this option is "useDefineForClassFields": true by default otherwise false. After changing tsconfig to this:

{
  "compilerOptions": {
    //....some options
    "module": "commonjs",
    "target": "es2022",
    "useDefineForClassFields": false,
    //....some options
}

Problem is gone!

ai-leonid avatar Apr 01 '24 21:04 ai-leonid