NestJS如何定义支持动态ID为键、值为对象数组的DTO结构
NestJS 实现动态顶层键结构的DTO定义
处理无固定名的动态顶层键,核心是通过TypeScript索引签名定义结构,结合NestJS默认集成的class-validator、class-transformer就能完成类型约束和运行时校验,具体实现步骤如下:
1. 定义数组项的基础DTO
首先定义动态键对应数组里的单个业务对象结构,注意修正你示例里surname后的笔误(原示例里写的是.,实际属性赋值用:):
import { IsString, IsNotEmpty } from 'class-validator'; export class BusinessItemDto { @IsString() @IsNotEmpty() name: string; @IsString() @IsNotEmpty() surname: string; }
2. 定义顶层动态键结构
根据业务场景选择对应写法:
场景1:仅需TS类型约束,不需要接口运行时校验
如果是内部方法传参、不需要对入参做自动校验,直接用TS接口定义索引签名即可:
interface UserIdBusinessMap { // 键为动态生成的字符串格式用户ID,值为业务对象数组 [userId: string]: BusinessItemDto[] }
这种写法下,你可以直接通过const data: UserIdBusinessMap = { 123456: [{name: 'lorem', surname: 'lorem'}] }的格式赋值,TS会自动做类型检查。
场景2:需要接口入参运行时校验(配合NestJS ValidationPipe)
如果是Controller层接收前端请求、需要自动校验参数格式,需要定义可被实例化的class,配合转换逻辑保证嵌套校验生效:
import { IsObject, Validate } from 'class-validator'; import { plainToInstance } from 'class-transformer'; import { BusinessItemDto } from './business-item.dto'; import { ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator'; // 可选:自定义校验器,校验顶层动态键符合用户ID格式 @ValidatorConstraint({ name: 'userIdKeyValidate', async: false }) class UserIdKeyValidate implements ValidatorConstraintInterface { validate(value: unknown) { if (typeof value !== 'object' || value === null) return false; // 这里可以自定义ID规则,示例为校验ID是纯数字字符串 return Object.keys(value).every(key => /^\d+$/.test(key)); } defaultMessage() { return '顶层键必须为合法的数字格式用户ID'; } } export class DynamicMapDto { @IsObject() @Validate(UserIdKeyValidate) [userId: string]: BusinessItemDto[]; // 配合ValidationPipe的transform选项做嵌套类型转换 static transform(value: unknown) { if (typeof value !== 'object' || value === null) return value; const instance = new DynamicMapDto(); for (const [userId, itemList] of Object.entries(value)) { instance[userId] = plainToInstance(BusinessItemDto, itemList as any[]); } return instance; } }
3. Controller层使用示例
开启ValidationPipe的自动转换即可自动完成校验:
import { Controller, Post, Body, ValidationPipe } from '@nestjs/common'; import { DynamicMapDto } from './dto/dynamic-map.dto'; @Controller('business') export class BusinessController { @Post('batch-update') batchUpdate( @Body(new ValidationPipe({ transform: true })) body: DynamicMapDto ) { // 直接通过用户ID访问对应数组即可,比如 body['123456'] return body; } }
内容的提问来源于stack exchange,提问作者Fabrizio Manna
相关产品推荐
相关产品推荐

