class-transformer中plainToClass不转换默认嵌套DTO问题求助
Class-transformer plainToClass无法转换类默认值中的嵌套对象问题
问题重现
当子类定义了嵌套数组的默认值,使用plainToClass转换不包含该属性的普通对象时,默认值中的对象不会被转换成指定的DTO类,仅保留为普通JS对象。这会导致开启class-validator的白名单验证时,该属性因不符合类型要求被移除;但如果转换时传入包含该属性的普通对象,嵌套对象则会被正常转换为目标DTO类型。
相关代码
父类定义:
export class NumberedSetDTO { @IsNotEmpty() @IsArray() @IsNumber({}, { each: true }) public reps: Array<number>; } export class ExerciseDto { @IsEnum(ExerciseName) public name!: ExerciseName; @IsArray() @ArrayMinSize(1) @ValidateNested({ each: true }) @Type(() => NumberedSetDTO) public sets?: Array<NumberedSetDTO>; }
子类(带默认值):
export class KSnatchDTO extends ExerciseDto { public name = ExerciseName.KSnatch; public sets= [{ reps: [10, 10, 10, 10, 10] }]; }
异常转换场景:
const workout = { name: 'k snatch', }; const cls = plainToClass(KSnatchDTO, workout, { enableImplicitConversion: true, }); // 此时 cls.sets 中的元素为普通对象,而非 NumberedSetDTO 实例
正常转换场景:
const workout = { name: 'k snatch', sets: [{ reps: [10, 12] }], }; const cls = plainToClass(KSnatchDTO, workout, { enableImplicitConversion: true, }); // 此时 cls.sets 中的元素为 NumberedSetDTO 实例
原因分析
class-transformer的plainToClass默认仅处理传入的普通对象中存在的属性,类自身的默认值初始化是JavaScript原生行为,不会触发@Type装饰器对应的嵌套类型转换逻辑。只有当属性存在于传入的普通对象中时,@Type才会生效,将嵌套对象转换为目标DTO类。
解决方案
1. 在构造函数中手动转换默认值
在子类构造函数里,直接将默认值转换为目标DTO实例,确保初始化时类型正确:
export class KSnatchDTO extends ExerciseDto { public name = ExerciseName.KSnatch; public sets: Array<NumberedSetDTO>; constructor() { super(); this.sets = plainToClass(NumberedSetDTO, [{ reps: [10, 10, 10, 10, 10] }], { enableImplicitConversion: true }); } }
2. 使用@Transform强制转换属性
在父类的sets属性上添加@Transform装饰器,无论属性来自传入对象还是类默认值,都强制触发转换:
import { Transform } from 'class-transformer'; export class ExerciseDto { @IsEnum(ExerciseName) public name!: ExerciseName; @IsArray() @ArrayMinSize(1) @ValidateNested({ each: true }) @Type(() => NumberedSetDTO) @Transform(({ value }) => plainToClass(NumberedSetDTO, value, { enableImplicitConversion: true }), { toClassOnly: true }) public sets?: Array<NumberedSetDTO>; }
3. 转换后手动校验并补全类型
在plainToClass转换完成后,检查目标属性的类型,若为普通对象则手动转换:
const cls = plainToClass(KSnatchDTO, workout, { enableImplicitConversion: true, }); // 检查并转换sets属性 if (cls.sets && cls.sets.length > 0 && !(cls.sets[0] instanceof NumberedSetDTO)) { cls.sets = plainToClass(NumberedSetDTO, cls.sets, { enableImplicitConversion: true }); }
内容的提问来源于stack exchange,提问作者Pascoe101
相关产品推荐
相关产品推荐

