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

NestJS自定义验证装饰器:解决null与undefined区分及提示问题

Solution for Custom Nullable Decorator with Clear Error Messages in NestJS

To fix the issue where the default IsOptional doesn't distinguish between null and undefined, and to create a reusable IsNullable decorator with explicit error messages, follow these practical approaches:

Approach 1: Type-Specific Nullable Decorator

Create a dedicated decorator for a specific data type (e.g., string, number) that allows null and provides clear validation feedback.

Step 1: Define the Custom Constraint

import { ValidatorConstraint, ValidatorConstraintInterface, ValidationArguments, registerDecorator, ValidationOptions } from 'class-validator';

@ValidatorConstraint({ name: 'isNullableString', async: false })
export class IsNullableStringConstraint implements ValidatorConstraintInterface {
  validate(value: any): boolean {
    // Allow null or non-empty string values
    return value === null || (typeof value === 'string' && value.trim() !== '');
  }

  defaultMessage(args: ValidationArguments): string {
    // Custom error message explicitly mentioning null is allowed
    return `${args.property} must be a non-empty string or null`;
  }
}

Step 2: Build the Decorator

export function IsNullableString(validationOptions?: ValidationOptions) {
  return function (target: Object, propertyName: string) {
    registerDecorator({
      target: target.constructor,
      propertyName: propertyName,
      options: validationOptions,
      validator: IsNullableStringConstraint,
    });
  };
}

Step 3: Use in Your DTO

import { IsOptional } from 'class-validator';

class UserDto {
  // Allows null or non-empty string; undefined will fail validation
  @IsNullableString()
  displayName: string | null;

  // Allows undefined (via IsOptional) or non-empty string; null will fail validation
  @IsOptional()
  @IsNullableString()
  bio?: string;
}

Approach 2: Generic Nullable Decorator (Wrap Any Validator)

For flexibility, create a higher-order function to wrap any existing class-validator decorator (e.g., IsNumber, IsEmail) and add nullable support with adjusted error messages.

Step 1: Create the Wrapper Function

import { ValidationOptions, ValidateIf } from 'class-validator';

export function makeNullable<T>(validator: (options?: ValidationOptions) => PropertyDecorator) {
  return function (validationOptions?: ValidationOptions) {
    return function (target: Object, propertyName: string) {
      // Skip validation if value is null
      ValidateIf((obj) => obj[propertyName] !== null)(target, propertyName);
      
      // Apply original validator with modified error message
      validator({
        ...validationOptions,
        message: validationOptions?.message || `${propertyName} must be valid or null`
      })(target, propertyName);
    };
  };
}

Step 2: Generate Nullable Validator Variants

import { IsString, IsNumber, IsEmail } from 'class-validator';

// Create nullable versions of common validators
const IsNullableString = makeNullable(IsString);
const IsNullableNumber = makeNullable(IsNumber);
const IsNullableEmail = makeNullable(IsEmail);

Step 3: Implement in Your DTO

class ProductDto {
  @IsNullableString({ message: 'Product name must be a string or null' })
  name: string | null;

  @IsNullableNumber()
  price: number | null;

  @IsNullableEmail()
  contactEmail: string | null;
}

Key Details

  • Distinguish null vs undefined:
    • Use IsNullable alone to allow null but reject undefined.
    • Combine with IsOptional to permit both null and undefined.
  • Custom Error Messages: Both approaches let you override default messages via the validationOptions parameter for full control over feedback.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 15:23:15