question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Is it possible to validate if only one of a group of properties are defined?

See original GitHub issue

Suppose I have something like this.

class UpdateUserDTO {

  @IsString()
  readonly status: string;

  @IsBoolean()
  readonly deleted: boolean;

  @IsString()
  readonly name: string;
}

I want to validate this so that the class can only have either status or deleted defined. It doesn’t matter if name is defined or not, but if status is defined, then deleted cannot be defined and vice versa.

Any way to make that work?

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:10
  • Comments:8 (2 by maintainers)

github_iconTop GitHub Comments

25reactions
KieranHarpercommented, Oct 29, 2019

Extending @halcarleton 's work above as suggested to combine the two decorators, the following works for me:

export function IncompatableWith(incompatibleSiblings: string[]) {
  const notSibling = IsNotSiblingOf(incompatibleSiblings);
  const validateIf = ValidateIf(
    incompatibleSiblingsNotPresent(incompatibleSiblings)
  );
  return function(target: any, key: string) {
    notSibling(target, key);
    validateIf(target, key);
  };
}
class UpdateUserDTO {

  @IncompatableWith(['deleted'])
  @IsString()
  readonly status: string;
  
  @IncompatableWith(['status'])
  @IsBoolean()
  readonly deleted: boolean;

  @IsString()
  readonly name: string;
19reactions
halcarletoncommented, Apr 22, 2019

It’s been a while since this issue was created, but here’s the solution I came up with for this use case. I had the same requirement, and it seems like a pretty common requirement.

What you’re looking to do would currently require the combination of a custom validator and ValidateIf. You end up with two validations, one validates if there a property present that cannot exist on the same instance as the validated property, and the other determines if a property should be validated.

// Define new constraint that checks the existence of sibling properties
@ValidatorConstraint({ async: false })
class IsNotSiblingOfConstraint implements ValidatorConstraintInterface {

  validate(value: any, args: ValidationArguments) {
    if (validator.isDefined(value)) {
      return this.getFailedConstraints(args).length === 0
    }
    return true;
  }

  defaultMessage(args: ValidationArguments) {
    return `${args.property} cannot exist alongside the following defined properties: ${this.getFailedConstraints(args).join(', ')}`
  }

  getFailedConstraints(args: ValidationArguments) {
    return args.constraints.filter((prop) => validator.isDefined(args.object[prop]))
  }
}

// Create Decorator for the constraint that was just created
function IsNotSiblingOf(props: string[], validationOptions?: ValidationOptions) {
  return function (object: Object, propertyName: string) {
    registerDecorator({
      target: object.constructor,
      propertyName: propertyName,
      options: validationOptions,
      constraints: props,
      validator: IsNotSiblingOfConstraint
    });
  };
}

// Helper function for determining if a prop should be validated
function incompatibleSiblingsNotPresent(incompatibleSiblings: string[]) {
  return function (o, v) {
    return Boolean(
      validator.isDefined(v) || // Validate if prop has value
      incompatibleSiblings.every((prop) => !validator.isDefined(o[prop])) // Validate if all incompatible siblings are not defined
    )
  }
}

Your class

class UpdateUserDTO {

  @IsString()
  @IsNotSiblingOf(['deleted'])
  @ValidateIf(incompatibleSiblingsNotPresent(['deleted']))
  readonly status: string;

  @IsBoolean()
  @IsNotSiblingOf(['status'])
  @ValidateIf(incompatibleSiblingsNotPresent(['status']))
  readonly deleted: boolean;

  @IsString()
  readonly name: string;
}

Note: there are definitely some improvements that can be made to this, but as a quick example it should get the job done.

If you wanted to you could wrap these two decorators in a decorator to make it a one line validation definition.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Validate Property Values - MATLAB & Simulink - MathWorks
When inheriting validation for a property from multiple classes, only a single Abstract property in one superclass can define the validation.
Read more >
How do I require one field or another or (one of two others) but ...
Remember that it means that "just one of these schemas can validate". The following schema achieves the property switching you are attempting to...
Read more >
Is there a way to validate that one of two properties (both ...
The object is valid if it matches one of two objects;. 1) The object requires an "id" and a "bar" array with at...
Read more >
Property form - General tab - Completing the Table fields
If an input value for a property doesn't match the table edits, the property name and the invalid value remain on the clipboard,...
Read more >
Schema validation reference for object types
data.schema object instance has properties which are not allowed by the schema: ["maximum"]. The schema defines an order_count property that expects ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found