'class-validator validate union type

I have a mongoose discriminator schema, which mean the data will be different according to one of the attributes.

class Feature {
  name: string
  option: ColorFeature|SizeFeature
}

class ColorFeature {
  kind: 'color'
  color: string
}

class SizeFeature {
  kind: 'size'
  size: number
}

What is the correct way to validate the Feature class so that it only accepts 2 different kinds?



Solution 1:[1]

it can be achieved by using validateNested() together with class-transformer discriminator

class BaseFeature {
  kind: 'size' | 'color'
}

class ColorFeature extends BaseFeature {
  kind: 'color'
  color: string
}

class SizeFeature extends BaseFeature {
  kind: 'size'
  size: number
}


class Feature {
  name: string

  @ValidateNested()
  @Type(() => BaseFeature, {
    keepDiscriminatorProperty: true,
    discriminator: {
      property: 'kind',
      subTypes: [
        { value: SizeFeature, name: 'size' },
        { value: ColorFeature, name: 'color' },
      ],
    },
  })
  option: SizeFeature | ColorFeature;
}

Solution 2:[2]

I spent a lot of time with this, and I finally found a cleaner way of doing it than the current answer.

Basically, the decorator @Type gives us some helper options if we want to use them, such us.. the object! So, you can return one type or the other conditionally, so the validation is done over one of the two types:

class Feature {
  name: string

  @ValidateNested()
  @IsDefined()
  @Type(({ object }) => {
    if(object.option?.kind === 'color') return ColorFeature;
    else if(object.option?.kind === 'size') return SizeFeature;
    // Handle edge case where the previous ifs are not fullfiled
  })
  option: ColorFeature | SizeFeature
}

You can even use a switch case for cleanliness sake in case you have more types:

  @ValidateNested()
  @IsDefined()
  @Type(({ object }) => {
    switch(object.option?.kind){
      case 'color':
        return ColorFeature;
      case 'size':
        return SizeFeature;
      case 'shape':
        return ShapeFeature;
      default:
        // Manage edge cases
    }
  })
  option: ColorFeature | SizeFeature | ShapeFeature

Then, you also have to use validation decorators in the extended classes, so that they are correctly validated.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Eduardo Poiate
Solution 2