class-transformer
class-transformer copied to clipboard
fix: apply custom transforms first for plainToClass / bypassing class transformers own transforms
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.
I think there is a stale PR #96 that basically tried to solve this, but never got merged.
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):
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;