如何通过纯装饰器方式实现扁平对象转嵌套class-transformer实例?
问题描述
我定义了如下3个类:
class Server { @Expose({ name: 'APP_PORT' }) @Type(() => Number) port: number @Expose({ name: 'APP_TAG' }) @Type(() => String) tag: string } class Database { @Expose({ name: 'DATABASE_PORT' }) @Type(() => Number) port: number @Expose({ name: 'DATABASE_NAME' }) @Type(() => String) name: string } class Global { @Type(() => Server) server: Server @Type(() => Database) database: Database }
使用以下代码将扁平结构的plainObject转换为类实例:
import { plainToInstance } from 'class-transformer' const plainObject = { APP_PORT: 5000, APP_TAG: '1.0.1', DATABASE_PORT: 8000, DATABASE_NAME: 'test', } const instance = plainToInstance(Global, plainObject) console.log(instance)
返回结果不符合预期:
Global { server: undefined, database: undefined, APP_PORT: 5000, APP_TAG: '1.0.1', DATABASE_PORT: 8000, DATABASE_NAME: 'test', }
期望得到的结果:
Global { server: { APP_PORT: 5000, APP_TAG: '1.0.1', }, database: { DATABASE_PORT: 8000, DATABASE_NAME: 'test', }, }
补充说明:曾通过修改plainObject结构临时解决,但希望仅通过调整类的装饰器实现需求。
解决方案
要实现从扁平对象到嵌套类实例的转换,需要给Global类的server和database属性添加@Transform装饰器,手动从扁平对象中提取对应字段并转换为子类实例,同时配合@Expose确保属性被正确处理。
修改后的完整类代码如下:
import { Expose, Type, plainToInstance, Transform } from 'class-transformer' class Server { @Expose({ name: 'APP_PORT' }) @Type(() => Number) port: number @Expose({ name: 'APP_TAG' }) @Type(() => String) tag: string } class Database { @Expose({ name: 'DATABASE_PORT' }) @Type(() => Number) port: number @Expose({ name: 'DATABASE_NAME' }) @Type(() => String) name: string } class Global { @Expose() @Type(() => Server) @Transform(({ obj }) => plainToInstance(Server, obj)) server: Server @Expose() @Type(() => Database) @Transform(({ obj }) => plainToInstance(Database, obj)) database: Database }
关键调整说明
@Expose():显式标记server和database属性为转换过程中需要处理的字段,避免被默认规则忽略。@Transform():利用转换上下文获取原始扁平对象,调用plainToInstance将整个扁平对象转换为对应子类的实例——因为子类的@Expose已经映射了对应的扁平字段名,会自动匹配提取所需字段。@Type():辅助工具识别子类的类型,保证类型转换的准确性。
验证与预期输出
运行原转换代码后,会得到嵌套的类实例:
Global { server: Server { port: 5000, tag: '1.0.1' }, database: Database { port: 8000, name: 'test' } }
如果需要输出时保留原始的APP_PORT等字段名而非类属性名,可以使用instanceToPlain处理转换后的实例:
import { instanceToPlain } from 'class-transformer' const instance = plainToInstance(Global, plainObject) console.log(instanceToPlain(instance))
输出结果将完全符合预期:
{ server: { APP_PORT: 5000, APP_TAG: '1.0.1', }, database: { DATABASE_PORT: 8000, DATABASE_NAME: 'test', }, }
内容的提问来源于stack exchange,提问作者8naf
相关产品推荐
相关产品推荐

