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

question: Transform decorator execute sequence

Open er230059 opened this issue 4 years ago • 3 comments

I was trying to...

class myDto {
      @Transform(() => console.log('second transform'))
      @Transform(() => console.log('first transform'))
      name: string;
}

The problem: According to TypeScript document (https://www.typescriptlang.org/docs/handbook/decorators.html#decorator-composition), when multiple decorators apply to a single declaration, their evaluation is similar to function composition in mathematics.

So I expected it should print the following log:

first transform
second transform

but actually, it prints in reverse sequence.

I think the reason is src/MetadataStorage.ts function findMetadatas https://github.com/typestack/class-transformer/blob/95541a404d6e4e84c26255e2ac2b2bb58bfcb52f/src/MetadataStorage.ts#L235 This function call the reverse() when it return.

return metadataFromAncestorsTarget
  .slice()
  .reverse()
  .concat((metadataFromTarget || []).slice().reverse());

I want to know is this behavior is expected correctly due to any reason? or it is a bug?

er230059 avatar May 19 '21 17:05 er230059

I'm also confused on this issue since the order of validators is different from the order of transformers, I always can not stop myself to mix them up :)

hsiaosiyuan0 avatar May 27 '21 07:05 hsiaosiyuan0

i don't understand, why this was done? Why do they work in different orders?

aau8 avatar Apr 04 '24 17:04 aau8

ok, I got it. Since class-transformer transforms an object into a class, it fires earlier. class-validator validates the class, so its decorators fire after class-transformer. Well, you can validate the value before converting it directly in the Transformer decorator. If the value is invalid, then call the exceptionFactory method, or, in the case of NestJS, you can throw out an instance of the HttpErrorByCode class. An example of how I did this to transform and validate a string in bigint.

// create-user.dto.ts
export class CreateUserDto {
  @IsNotExists([ 'user', 'tgid' ])
  @TransformStringToBigInt()
  @ApiProperty({ type: 'string' })
  tgid: bigint;
}
import { Transform, TransformOptions } from 'class-transformer';
import { isIntString } from './IsIntString';
import { HttpErrorByCode } from '@nestjs/common/utils/http-error-by-code.util';
import { HttpStatus } from '@nestjs/common';

export const TransformStringToBigInt = (options: TransformOptions = {}) => {
  return Transform(({ value, key }) => {
    if (!isIntString(value)) {
      throw new HttpErrorByCode[HttpStatus.BAD_REQUEST]([
        `${key} the property must be a string consisting of numbers only`,
      ]);
    }

    return BigInt(value);
  }, options);
};

aau8 avatar Apr 05 '24 15:04 aau8