如何让class-transformer在源对象不匹配目标类时抛出错误?
解决class-transformer plainToInstance返回空实例及动态转换问题
一、阻止空实例生成的核心思路
plainToInstance本身的设计是尽量完成转换,即使源数据类型不符(比如传入字符串)也会返回空实例,不会主动抛出错误。要阻止这种行为,必须结合class-validator的验证逻辑,在转换前后对数据结构做校验,不符合规则则直接抛出错误。
二、转换前用class-validator验证源数据
class-validator支持直接验证普通对象(无需先转成类实例),可以先校验源数据的基础结构,不合格就终止转换流程:
1. 定义验证规则
先给KikGenericResponse添加验证装饰器,明确结构要求:
import { IsObject, IsNumber, IsOptional, ValidateNested, Type } from 'class-validator'; import { plainToInstance, ClassConstructor } from 'class-transformer'; // 通用响应类 class KikGenericResponse<T> { @IsOptional() @IsNumber() code?: number; @IsObject() @ValidateNested() // 若data有固定类型,可替换为具体类,比如Type(() => User) @Type(() => Object) data: T; }
2. 编写前置校验函数
先检查源数据的基础类型,再用class-validator验证结构:
import { validateSync } from 'class-validator'; function validatePlainData<T>(plainData: unknown, cls: ClassConstructor<T>): void { // 基础类型校验:非对象/直接为null,直接报错 if (typeof plainData !== 'object' || plainData === null) { throw new Error('无效数据:必须为对象类型'); } // 验证结构是否符合目标类要求 const errors = validateSync(plainToInstance(cls, plainData), { skipMissingProperties: false }); if (errors.length > 0) { throw new Error(`结构校验失败:${JSON.stringify(errors)}`); } }
三、实现动态转换逻辑
针对「匹配KikGenericResponse则转data,否则转成KikGenericResponse」的需求,结合校验逻辑实现:
// 判断是否为KikGenericResponse结构 function isKikGenericResponse(plainData: unknown): plainData is KikGenericResponse<unknown> { return typeof plainData === 'object' && plainData !== null && 'data' in plainData; } // 动态转换函数 function dynamicTransform<T>(plainData: unknown, targetDataClass: ClassConstructor<T>): KikGenericResponse<T> { // 处理非对象类型的源数据 if (typeof plainData !== 'object' || plainData === null) { const response = plainToInstance(KikGenericResponse, { data: plainData }); const errors = validateSync(response); if (errors.length > 0) { throw new Error(`响应结构无效:${JSON.stringify(errors)}`); } return response; } // 匹配KikGenericResponse结构,转换内部data if (isKikGenericResponse(plainData)) { validatePlainData(plainData, KikGenericResponse); const dataInstance = plainToInstance(targetDataClass, plainData.data); const dataErrors = validateSync(dataInstance); if (dataErrors.length > 0) { throw new Error(`数据结构无效:${JSON.stringify(dataErrors)}`); } return { ...plainData, data: dataInstance } as KikGenericResponse<T>; } // 不匹配则包装成KikGenericResponse const response = plainToInstance(KikGenericResponse, { data: plainData }); const errors = validateSync(response); if (errors.length > 0) { throw new Error(`响应结构无效:${JSON.stringify(errors)}`); } return response; }
四、关键注意事项
- 若不需要异步校验,使用
validateSync即可;如果有异步验证规则(比如数据库查重),则改用validate配合async/await。 - 可根据实际业务调整
skipMissingProperties、forbidNonWhitelisted等验证选项,比如设置forbidNonWhitelisted: true来禁止源数据包含类定义外的属性。 - 泛型
T的具体类型可在调用时传入,比如dynamicTransform(plainData, User)将data转换为User实例。
内容的提问来源于stack exchange,提问作者OtaconKiko
相关产品推荐
相关产品推荐

