Validate a discriminated union type using class-validator

I am working with a mongoose discriminator schema where the data will vary based on one of the attributes.

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

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

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

How can I properly validate the Feature class so it only accepts 2 different kinds?

Answer №1

One way to accomplish this is by utilizing the validateNested() function in conjunction with a 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;
}

Answer №2

After dedicating a significant amount of time to this task, I have discovered a more efficient approach compared to the existing solution.

The @Type decorator provides helpful options, such as working with objects. This allows for conditional return of different types based on specific criteria, enabling validation 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 when previous conditions are not met
  })
  option: ColorFeature | SizeFeature
}

To maintain cleanliness and manage multiple types effectively, consider using a switch case or a Record structure:

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

Furthermore, ensure that validation decorators are also applied in the extended classes to guarantee correct validation processes.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The types for Cypress are not being detected by my Angular tsconfig file

I'm facing an issue with my Angular tsconfig not detecting the Cypress 12.3 types. I have tried numerous solutions to resolve this problem, but nothing seems to work, except for the extreme measure of starting the project over, which I think might sol ...

Show a modal when the axios request is finished

EDIT: After some exploration, I've shared the solution I found in case it helps others facing a similar issue. I'm currently working on building a reusable Bootstrap 5 modal child component within Vue3. The goal is to pass an ID as a prop from t ...

The formControl value is not being shown on the display

I am facing an issue with a form that has multiple fields created using formGroup and formControlName. The challenge arises when I launch the application and need to retrieve data from the back end to display it on the view. The specific problem I'm ...

Data loss occurs when the function malfunctions

Currently, I am working with Angular version 11. In my project, I am utilizing a function from a service to fetch data from an API and display it in a table that I created using the ng generate @angular/material:table command. Client Model export interfac ...

The function for batch insertion only functions with Postgresql and SQL Server databases

I am a beginner in JavaScript and I am currently working on creating a new restaurant. I have come across a code snippet that inserts a relation into a join-table: await newRestaurant.$relatedQuery('tags', trx).relate(tagIds); Is it not possible ...

Angular 16 HttpClient post request with asynchronous action

Here I am working on integrating JWT in Angular with a .Net Core API. When I start my Angular server and launch the application, I encounter the following scenarios: Attempting with correct credentials initially fails, but retrying allows it to work. Tryi ...

Received a JSON value that cannot be converted into a tuple of two numbers in Typescript

I'm a beginner when it comes to TypeScript and I am struggling to find the solution to my question. In my project, there is a config.json file structured like this: { "api": { "baseUrl": "http://localhost:9000/api", "list": { "itemsP ...

Incorporating Parse into Angular 2 using TypeScript

While I am aware that angular2 is still in beta, I decided to give it a try. I followed the angular2 quickstart guide available at https://angular.io/docs/js/latest/quickstart.html and everything seemed to be working fine. Now, I wanted to integrate Parse ...

VSCode prioritizes importing files while disregarding any symbolic links in order to delve deeper into nested node

I'm encountering a problem with VSCode and TypeScript related to auto imports. Our application includes a service known as Manager, which relies on certain functions imported from a private npm package called Helpers. Both Manager and Helpers make us ...

Library for Nodejs that specializes in generating and converting PDF/A files

Is there a library available that can convert/create a PDF/A file? I've been searching for solutions but the existing answers suggest using an external service or provide no response at all. I heard about libraries in other languages like ghostscriptP ...

Checkbox offering a tri-state option

Seeking help on creating a checkbox with three states. I have implemented a method to toggle between these states upon clicking. However, the issue is that this method is only triggered after the HTML changes. As a result, the checkbox's navigation be ...

Leveraging TypeScript in tandem with React to create stateless components

I'm curious about the distinctions between these variations, in a scenario where state is not being used: 1. export class SkillList extends React.Component<SkillListProps> {} 2. export class SkillList extends React.Component<SkillListProps, ...

Unable to Add Stripe Client in NestJS using (https://www.npmjs.com/package/@golevelup/nestjs-stripe)

I'm currently facing an issue while trying to integrate the GoLevelUp stripe package into my NestJs project. Although I can successfully import the package into my global app module, I'm struggling to inject a functional client into the designate ...

The TS2345 error is triggered when using the fs.readFile function with specified string and

Attempting to utilize the fs.readFile method in TypeScript, my code looks like this... import {readFile} from 'fs'; let str = await readFile('my.file', 'utf8'); This results in the following error message: TS2345: Argumen ...

I'm receiving an error message stating "mongoose.connect is not a function" while attempting to establish a connection with mongoose. Can you help me troub

I'm a beginner in Node.js and I'm currently working on creating a node/express/mongoose server app using TypeScript. Below is my app.ts file: // lib/app.ts import express from 'express'; import * as bodyParser from 'body-parser&a ...

Animating nested child routes with framer-motion in react-router-dom version 6.4.3



I recently updated to the latest version of react-router-dom and their new Data APIs, which required me to rewrite my app's routing logic using object-based routing instead of the V5 JSX syntax with Routes. I'm curious if anyone has been su ...

Enhance your coding experience with Angular Apollo Codegen providing intelligent suggestions for anonymous objects

Currently, I am exploring the integration of GraphQL with Angular. So far, I have been able to scaffold the schema successfully using the @graphql-codegen package. The services generated are functional in querying the database. However, I've noticed ...

What is the purpose of specifying the data types of my method parameters while I am incorporating an interface?

For instance: interface Foo { someProperty: Number someMethod?: (str: string) => void } class Bar implements Foo { someProperty = 42 someMethod (str) { console.log(this.someProperty) } } The str argument in someMethod() is clearly a str ...

What steps should I take to address conflicting type identifiers between Cypress and jQuery?

Currently, I am tasked with writing TypeScript end-to-end tests for an Angular 11 application. Following the recommended practices of Cypress, my test setup is encountering a conflict due to existing jQuery dependencies (3.5.1) in the app and Cypress (8.4. ...

Angular 2 - Creating Your Own Validation Rules

Could someone please clarify the TypeScript syntax shown below for me? {[s: string]: boolean} This is the return type for a ValidatorFn in Angular 2. What exactly does the array: [s: string] represent? When creating my own custom ValidatorFn function, w ...