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

fix: apply custom transforms first for plainToClass / bypassing class transformers own transforms

Open florianbepunkt opened this issue 4 years ago • 3 comments

Description

When calling classToPlain custom transforms are applied before the built in one. But using plainToClass it is the other way around: https://github.com/typestack/class-transformer/blob/d2ba760fd7acf9baafc5e3304c433049df55f116/src/TransformOperationExecutor.ts#L285

Not sure if this is intentional, but I have a use case where the deserialization should only happen in a custom @Transform decorator function. Simplified I do something like this:

abstract class BaseEntity<T> {
    private state: T;

    // this should not be called by class transformer, but is
    protected constructor(Schema: ClassType<T>, dto: T, id?: string) {
        const state = dto instanceof Schema ? dto : plainToClass(Schema, dto);
        this.state = state;
    }
}

class SomeEntitySchema {
    foo: string;
}

class SomeEntity extends BaseEntity<SomeEntitySchema> {
  // this should not be called by class transformer, but is
  private constructor(dto: SomeEntitySchema, id?: string) {
    super(SomeEntitySchema, dto, id);
  }

  public static create(dto: SomeEntitySchema, id?: string) {
    const instance = new SomeEntity(dto, id);
    return instance;
  }
}

class ThatReferencesThis {
  @Transform((value: any) => SomeEntity.create(value), { toClassOnly: true })
  someEntity: SomeEntity;
}

However, before the transform is called the built-in ones are applied, among them this line https://github.com/typestack/class-transformer/blob/d2ba760fd7acf9baafc5e3304c433049df55f116/src/TransformOperationExecutor.ts#L140

which creates a new instance of my class

Expected behavior

The constructors of SomeEntity and BaseEntity should not be called by class transformer. It would be great if there would be a way to delegate serializing/deserializing a property to a custom method only (bypassing class transformers own transforms)

Actual behavior

They do get called.

florianbepunkt avatar Aug 03 '20 18:08 florianbepunkt

I think there is a stale PR #96 that basically tried to solve this, but never got merged.

florianbepunkt avatar Aug 03 '20 18:08 florianbepunkt

I think it is a bug. I think custom transformation should be after default one. Current code(there is applyCustomTransformations then transform in the first block but there is transform then applyCustomTransformations in last one): image

Nikolay-Uvarov avatar May 06 '21 11:05 Nikolay-Uvarov

This bug still exists. My workaround is to remove the Type decorator:

@IsDateString()
@Transform(transformToDate, { toClassOnly: true })
@Transform(transformFromDate, { toPlainOnly: true })
// Removing this decorator ensures only the custom transformers are used.
// @Type(() => Date)
disabled_at: Date;

clintonb avatar Feb 07 '24 08:02 clintonb