使用Decimal作为默认值时plainToInstance无法正常工作的问题排查
问题分析与解决方案
你的问题并非class-transformer的bug,而是由于对属性处理逻辑或Transform装饰器参数的理解偏差,或是使用了较旧版本的class-transformer导致的。
核心原因
在早期版本的class-transformer中,@Transform装饰器的obj参数指向的是正在创建的类实例,而非传入的普通对象。由于class-transformer默认不会调用类的构造函数,实例的totalDistance和attempts属性在Transform执行时还未被赋值(处于undefined状态),因此调用new Decimal(obj.totalDistance)会抛出参数无效的错误。
即使在新版本中,若属性处理顺序导致Transform先于其他属性赋值执行,也可能出现类似问题,但新版本已将obj参数改为指向传入的普通对象,因此可以直接获取到正确的totalDistance和attempts值。
解决方案
方案1:升级class-transformer到最新版本
升级到v0.5及以上版本后,原有代码即可正常工作,因为obj参数会指向传入的普通对象:
import { plainToInstance, Transform } from 'class-transformer'; import Decimal from 'decimal.js'; class MyClass { readonly attempts: number = 1; readonly totalDistance: number = 1; @Transform(({ obj }) => new Decimal(obj.totalDistance).div(obj.attempts)) readonly distancePerAttempt: Decimal = new Decimal(0); } const instance = plainToInstance(MyClass, { attempts: 3, totalDistance: 30 }); console.log(instance.distancePerAttempt.toString()); // 输出 "10"
方案2:使用Getter属性替代Transform装饰器
如果无法升级版本,可将distancePerAttempt改为Getter属性,它会在访问时自动计算值,无需依赖Transform:
import { plainToInstance } from 'class-transformer'; import Decimal from 'decimal.js'; class MyClass { readonly attempts: number = 1; readonly totalDistance: number = 1; get distancePerAttempt(): Decimal { return new Decimal(this.totalDistance).div(this.attempts); } } const instance = plainToInstance(MyClass, { attempts: 3, totalDistance: 30 }); console.log(instance.distancePerAttempt.toString()); // 输出 "10"
方案3:通过构造函数计算属性值
使用useConstructor: true选项让class-transformer调用类的构造函数,在构造函数中直接计算distancePerAttempt:
import { plainToInstance } from 'class-transformer'; import Decimal from 'decimal.js'; class MyClass { readonly attempts: number; readonly totalDistance: number; readonly distancePerAttempt: Decimal; constructor({ attempts = 1, totalDistance = 1 }: Partial<MyClass> = {}) { this.attempts = attempts; this.totalDistance = totalDistance; this.distancePerAttempt = new Decimal(totalDistance).div(attempts); } } const instance = plainToInstance(MyClass, { attempts: 3, totalDistance: 30 }, { useConstructor: true }); console.log(instance.distancePerAttempt.toString()); // 输出 "10"
内容的提问来源于stack exchange,提问作者Serhii Pemakhov
相关产品推荐
相关产品推荐

