You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NestJS中实现键为枚举类型、值结构一致的对象动态验证(避免枚举变更时同步更新DTO)

Solution: Dynamic Validation for Enum-Keyed Objects in Class-Validator

I totally get your frustration—having to manually update your Locations class every time you add a new country to the enum is tedious and error-prone. Let's fix this with a dynamic approach that syncs automatically with your Countries enum, no more manual DTO updates required.

Step 1: Create a Custom Validator for Enum Keys

First, we'll build a custom validator that checks if all keys in the locations object are valid members of your Countries enum. This ensures no invalid country codes slip through.

import { registerDecorator, ValidatorConstraint, ValidatorConstraintInterface, ValidationOptions, ValidationArguments } from 'class-validator';
import { Countries } from './your-enum-path';

// Define the core validation logic
@ValidatorConstraint({ async: false })
class ValidCountryKeysConstraint implements ValidatorConstraintInterface {
  validate(obj: Record<string, unknown>, args: ValidationArguments) {
    const validCountryCodes = Object.values(Countries);
    // Verify every key in the object matches a valid enum value
    return Object.keys(obj).every(key => validCountryCodes.includes(key as Countries));
  }

  defaultMessage(args: ValidationArguments) {
    return `Invalid country key in ${args.property}. Valid options: ${Object.values(Countries).join(', ')}`;
  }
}

// Wrap the constraint in a reusable decorator for easy use
export function ValidCountryKeys(validationOptions?: ValidationOptions) {
  return function (target: object, propertyName: string) {
    registerDecorator({
      target: target.constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: ValidCountryKeysConstraint,
    });
  };
}

Step 2: Update Your DTO to Use Dynamic Validation

Now, replace your hardcoded Locations class with a Record type, and apply our custom validator along with nested validation for the Country structure.

import { IsObject, ValidateNested, IsOptional, Type } from 'class-validator';
import { Country } from './country.dto';
import { Countries } from './your-enum-path';
import { ValidCountryKeys } from './valid-country-keys.validator';

export class CreateDto {
  // ... your other properties here

  @IsObject()
  @ValidCountryKeys() // Ensures all keys are valid Countries enum values
  @ValidateNested({ each: true }) // Validate every value in the object
  @Type(() => Country) // Apply Country class validation rules to each value
  @IsOptional()
  readonly locations?: Record<Countries, Country>;
}

How This Works

  • Dynamic Enum Sync: The custom validator pulls valid country codes directly from the Countries enum. Add a new member (like FR = 'FR') to the enum, and the validator will automatically recognize it—no DTO changes needed.
  • Consistent Value Structure: The @ValidateNested({ each: true }) and @Type(() => Country) decorators ensure every value in the locations object adheres to your Country class rules, just like your original implementation.
  • Clear Error Messages: If someone sends an invalid country code (e.g., FR before it's added to the enum), the validator will return a friendly error listing all valid options.

Testing with Your Example Payload

This setup will correctly validate your sample request:

{ 
  // ... other props
  "locations": { "IT": { "region": ["Lazio"] } }
}

And it will reject requests with invalid keys, like:

{ 
  "locations": { "FR": { "region": ["Île-de-France"] } }
}

With an error message: Invalid country key in locations. Valid options: ES, IT

Key Benefits

  • Maintenance-Free: No more syncing your DTO with enum changes
  • Reusable: The ValidCountryKeys decorator can be used anywhere you need to validate an object with enum keys
  • Type-Safe: The Record<Countries, Country> type gives you TypeScript intellisense for valid country codes

内容的提问来源于stack exchange,提问作者fasenderos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 21:57:29