class-transformer校验多类型对象数组时如何配置@Type装饰器
class-validator 联合类型数组嵌套校验实现方案
@Type装饰器原生默认仅支持返回单一类型,要覆盖多类型联合的嵌套校验,可根据你的入参特征选择以下两种实现方案:
方案1:带判别字段的原生@Type实现(推荐)
这是class-transformer官方支持的多态类型处理方式,性能高、无校验误判,前提是数组中不同类型的元素必须携带一个固定的、值不重复的判别字段(比如type/kind字段,用不同枚举值标记元素所属类型)。
- 首先将联合类型的每个分支定义为独立的DTO类,配置对应校验规则,固定判别字段的值:
import { IsString, IsNumber, IsDefined } from 'class-validator'; class ObjA { // 判别字段,ObjA类型的元素该字段值固定为'a' @IsDefined() readonly type: 'a'; @IsString() readonly aField: string; // ObjA独有的业务字段 } class ObjB { // 判别字段,ObjB类型的元素该字段值固定为'b' @IsDefined() readonly type: 'b'; @IsNumber() readonly bField: number; // ObjB独有的业务字段 }
- 在
@Type装饰器的回调中,根据当前处理元素的判别字段返回对应类:
import { IsArray, ArrayNotEmpty, ValidateNested } from 'class-validator'; import { Type } from 'class-transformer'; class ExampleDTO { @IsArray() @ArrayNotEmpty() @ValidateNested({ each: true }) @Type((context) => { // context.object为数组中当前正在转换的单个入参元素 switch (context.object.type) { case 'a': return ObjA; case 'b': return ObjB; default: // 无法识别类型时返回空类,后续校验会自动抛出格式错误 return class {}; } }) readonly mixedArray: Array<ObjA | ObjB>; }
方案2:自定义校验器实现(无判别字段场景)
如果入参没有统一的判别字段,可以自定义校验规则,依次尝试用每个分支类型校验元素,只要匹配其中一个类型即判定为合法。
- 编写自定义联合类型校验器:
import { ValidatorConstraint, ValidatorConstraintInterface, Validate, IsArray, ArrayNotEmpty } from 'class-validator'; import { plainToInstance } from 'class-transformer'; import { validateSync } from 'class-validator'; @ValidatorConstraint({ name: 'mixedTypeArrayValidator', async: false }) class MixedTypeArrayValidator implements ValidatorConstraintInterface { validate(value: unknown) { if (!Array.isArray(value)) return false; return value.every((item) => { // 先尝试匹配ObjA规则 const aInstance = plainToInstance(ObjA, item); if (validateSync(aInstance).length === 0) return true; // ObjA不匹配则尝试匹配ObjB规则 const bInstance = plainToInstance(ObjB, item); return validateSync(bInstance).length === 0; }); } defaultMessage() { return '数组元素不符合ObjA或ObjB的格式要求'; } }
- 在DTO中直接使用该校验器,无需配置
@Type和@ValidateNested:
class ExampleDTO { @IsArray() @ArrayNotEmpty() @Validate(MixedTypeArrayValidator) readonly mixedArray: Array<ObjA | ObjB>; }
该方案的缺点是类型分支越多校验性能越差,如果两个分支类型存在字段重叠(比如有同名同类型的公共字段,但其他字段规则不同),可能出现误判。
注意事项
- TS的
type、interface会在编译后被擦除,无论用哪种方案,都必须将联合类型的每个分支定义为带class-validator装饰器的实体类,否则校验规则不会生效。 - 如果在NestJS等框架中使用全局校验管道,需要开启
transform: true配置,否则@Type装饰器的转换逻辑不会执行。 - 优先选择带判别字段的方案,可维护性和稳定性远高于自动匹配方案。
内容的提问来源于stack exchange,提问作者Mido
相关产品推荐
相关产品推荐

