使用class-validator验证Record<string, string[]>失败求助
问题:class-validator自定义IsRecord装饰器验证Record<string, string[]>类型无效
尝试用class-validator验证Record<string, string[]>类型数据,但自定义的IsRecord装饰器未生效。以下是相关代码:
自定义IsRecord装饰器代码
import { isObject, registerDecorator, ValidationArguments, ValidationOptions, } from 'class-validator'; export const IsRecord = ( key_validators: ((value: string) => boolean)[], value_validators: ((value: string[]) => boolean)[], validationOptions?: ValidationOptions, ) => { return function (object: unknown, propertyName: string) { registerDecorator({ name: 'IsRecord', target: (object as any).constructor, propertyName: propertyName, options: validationOptions, validator: { validate(value: unknown, args: ValidationArguments) { if (!isObject(value)) return false; const keys = Object.keys(value); return keys.some(key => { // eslint-disable-next-line @typescript-eslint/naming-convention const is_key_invalid = key_validators.some( validator => !validator(key), ); if (is_key_invalid) return false; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore return value_validators.some(validator => !validator(value[key])); }); }, }, }); }; };
DTO定义代码
import { BaseRole, ModuleName, Topic, } from 'my-package'; import { IsBoolean, IsEnum, IsObject, IsOptional } from 'class-validator'; import { IsRecord } from './decorators/is-record.decorator'; export class AutoRegisterDto { @IsEnum(ModuleName) name: ModuleName; @IsBoolean() isEnabled: boolean; @IsObject() @IsOptional() @IsRecord([], []) actions: Record<Topic, BaseRole[]>; }
相关枚举与类型定义
BaseRole枚举
export declare enum BaseRole { UNREGISTERED_GUEST = "unregistered_user", UNVERIFIED_GUEST = "unverified_guest", VERIFIED_GUEST = "verified_guest", STUDENT = "student", EMPLOYEE = "employee", ADMIN = "admin" }
Topic类型
export declare type Topic = string;
问题原因与修复方案
你的装饰器存在两个核心逻辑错误:
- 遍历逻辑颠倒:使用
keys.some()会导致只要有一个键值对"符合条件"就返回true,这和"所有键值对都必须通过验证"的需求完全相反,应该替换为keys.every()。 - 验证判断逻辑错误:
value_validators.some(validator => !validator(value[key]))的逻辑是"只要有一个验证器不通过就返回true",这会导致验证失败的键值对被误判为有效,需要改为验证所有验证器都通过。
修复后的IsRecord装饰器代码:
import { isObject, registerDecorator, ValidationArguments, ValidationOptions, } from 'class-validator'; export const IsRecord = ( key_validators: ((value: string) => boolean)[], value_validators: ((value: string[]) => boolean)[], validationOptions?: ValidationOptions, ) => { return function (object: unknown, propertyName: string) { registerDecorator({ name: 'IsRecord', target: (object as any).constructor, propertyName: propertyName, options: validationOptions, validator: { validate(value: unknown, args: ValidationArguments) { if (!isObject(value)) return false; const entries = Object.entries(value); // 确保所有键值对都通过验证 return entries.every(([key, val]) => { // 所有键验证器都必须通过 const allKeysValid = key_validators.every(validator => validator(key)); if (!allKeysValid) return false; // 所有值验证器都必须通过 const allValuesValid = value_validators.every(validator => validator(val)); return allValuesValid; }); }, }, }); }; };
另外,你当前调用@IsRecord([], [])时传入了空数组,意味着没有任何实际验证逻辑。如果要验证BaseRole[]中的元素都是有效枚举值,可以添加对应验证逻辑:
// 新增BaseRole数组验证函数 const isBaseRoleArray = (value: string[]): boolean => { return value.every(item => Object.values(BaseRole).includes(item)); }; // 在DTO中修改装饰器调用 @IsOptional() @IsRecord([], [isBaseRoleArray]) actions: Record<Topic, BaseRole[]>;
内容的提问来源于stack exchange,提问作者Ibrahim
相关产品推荐
相关产品推荐

