Creating a sophisticated union type by deriving it from another intricate union type

I have a unique data structure that includes different types and subtypes:

type TypeOne = {
  type: 'type-one',
  uniqueKey1111: 1,
};
type TypeTwo = {
  type: 'type-two',
  uniqueKey2222: 2,
}
type FirstType = {
  type: 'first',
  details: TypeOne | TypeTwo
}
type SubTypeOne = {
  type: 'sub-type-one',
  uniqueKey3: 3,
};
type SubTypeTwo = {
  type: 'sub-type-two'
};
type SecondType = {
  type: 'second',
  details: SubTypeOne | SubTypeTwo,
}
type DataStructure = FirstType | SecondType;

The data has main types and respective subtypes, where selecting a main type restricts the subtype selection,

On my page, I have two dropdown selects - one for choosing the main type and another for selecting the subtype based on the selected main type.

I am trying to validate these select options:

To help with this, I have a generic method that converts the DataStructure type to SelectOption format:

type SelectOption<T> = { name: string, value: T };

I am unsure how to validate additional options whose values depend on the main select options

type SelectOptions = {
  main: SelectOption<DataStructure['type']>[],
  additional: SelectOption<DataStructure['details']['type']>[];
}

const options : SelectOptions = {
  main: [{name: 'name', value: 'first'}],
  additional: [{name: '', value: 'second-two'}],
}

The current options contain invalid data, and TypeScript does not check if additionalOptions contain incompatible options with the main.

It seems like the current SelectOptions structure cannot handle this validation.

I would appreciate any ideas on how to solve this problem.

I believe the options should have the following structure:

const options  = {
  main: [{name: 'name', value: 'first'}, {name: 'name', value: 'second'}],
  additional: {
    first: [{name: '', value: 'first-one'}, {name: '', value: 'first-two'}],
    second: [{name: '', value: 'second-one'}, {name: '', value: 'second-two'}]
  },
};

Answer №1

The desired structure for SelectOptions can be achieved with the following TypeScript code:

type SelectOptions = {
    main: SelectOption<"first">[];
    additional: SelectOption<"first-one">[] | SelectOption<"first-two">[];
} | {
    main: SelectOption<"second">[];
    additional: SelectOption<"second-one">[] | SelectOption<"second-two">[];
}

To compute SelectOptions based on DataType, we need to distribute the definition across the unions in DataType.


In distributing across unions, there are two known methods. One is using distributive conditional types, where a type of the form

type D<T> = T extends any ? F<T> : never

is utilized to automatically break down the union constituents before calculation. The other method involves using mapped types and immediately indexing into them to obtain a union of values.

The proposed SelectOptions definition demonstrates distribution over the DataType union as well as further distribution over the DT['details'] union for each piece DT within the union.

This approach ensures that incorrect data is rejected while correct data is accepted.

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 RxJS race function comes to a standstill if neither stream completes

Consider the code snippet below: import { interval, race, Subject } from 'rxjs'; import { mapTo } from 'rxjs/operators'; const a$ = new Subject<number>(); const b$ = interval(1000).pipe(mapTo(1)); race([a$, b$]).subscribe(consol ...

Using TypeScript for Type Inference in Fetch Operations

I created a custom Fetch wrapper in Typescript to fetch data from my API. I am facing an issue where the retrieved data is of type "any" and I want to convert it to a specific type, like "user". Here is the link to my codesandbox for reference: https://co ...

Unusual Behavior of *ngIf and jQuery in Angular 5: A curious case

I'm encountering a strange issue when using the expand-collapse feature of Bootstrap 4 with *ngIf for expansion and collapse. I noticed that the jQuery doesn't work when *ngIf is used, but it works fine when *ngIf is removed. HTML: <div cla ...

Is there a way to automatically scroll 50 pixels down the page after pressing a button?

Is there a way to make my page scroll down in Angular when a button is clicked? I attempted to use this code, but it didn't have the desired effect. What is the best method for scrolling the page down by 50px? window.scrollBy(0, 50); ...

Is there a way to automatically incorporate a component into every view within a Next.js application?

Is there a more efficient and less cumbersome way to import components for every view in a Next.js app? I am interested in incorporating the "arwes" framework into my project and utilizing components from . One of the examples of a component I will be usin ...

When using html2canvas in Angular, it is not possible to call an expression that does not have a call signature

I'm currently working on integrating the html2canvas library into an Angular 8 project. Despite trying to install the html2canvas types using npm install --save @types/html2canvas, I'm still facing issues with its functionality. Here's how ...

Incorporate an image icon into an Angular grid

Currently, I am in the process of building a web application using Angular. The main goal is to create a grid and color specific cells based on data input. Below is the snippet of my HTML code: <mat-grid-list cols="10"> <mat-grid-tile * ...

Issue during deployment: The type 'MiniCssExtractPlugin' cannot be assigned to the parameter type 'Plugin'

I'm working on deploying a Typescript / React project and have completed the necessary steps so far: Created a deployment branch Installed gh-pages for running the deployed application Added a deploy command as a script in the package.j ...

Error TS2322: Type 'Partial<T>' is not assignable to type 'T'

I'm struggling to articulate my problem, so I think the best way to convey it is through a minimal example. Take a look below: type Result = { prop1: { val1: number, val2: string }, prop2: { val1: number } }; f ...

Pixijs is unable to load spritesheets correctly

I am currently facing an issue while trying to load a spritesheet in PixiJS following the instructions provided on Below is the code snippet I am using: PIXI.Loader.shared.add('sheet', require('../assets/spritesheet.json')).load(sprite ...

Unable to swap out string with text box in TypeScript

I am trying to swap __ with a text box in Angular 2/4. Take a look at the example provided in the link below. https://stackblitz.com/edit/angular-ajkvyq?file=app%2Fapp.component.ts ...

InvalidAction: The function forEach cannot be applied to "res"

Here is the HTML code that I am currently working with: <div *ngIf="chart" class="col-xl-4 col-lg-6"> <div class="card cardColor mb-3"> <div class="card-header headColor"> <img class="img-fluid" src="../../../ ...

Angular throwing an error message: "ChildrenOutletContexts provider not found!"

I developed a basic testing application and encountered the error message - "No provider for ChildrenOutletContexts!" I have searched through various related posts but to no avail. Here is my project structure: The App Module contains the App Routing Modu ...

Issue with retrieving JSON objects in Next.js

I've been developing a straightforward crypto price tracker using the coingecko API. At the moment, my code is unable to retrieve any of the JSON objects from the API link, and curiously, no errors or warnings are being generated to indicate what the ...

Sending an ID from an array within a *ngFor loop to a different component in Angular: a step-by-step guide

I have a collection of items that I loop through using ngFor. My goal is to pass all its attributes to another component. I attempted to accomplish this with the following code: *ngFor='let item of postList [routerLink]="['/detailed-post&ap ...

Sometimes, Express may return the message "not found" on and off

After working with express for many years, I find myself a bit out of practice with TypeScript - and it seems like my eyesight is failing me! This is the first time I've encountered this issue, so I must be missing something... My current dilemma is ...

React/Typescript - Managing various event types within a unified onChange function

In my current project, I am working with a form that includes various types of input fields using the mui library. My goal is to gather the values from these diverse input components and store them in a single state within a Grandparent component. While I ...

Issue encountered when attempting to access disk JSON data: 404 error code detected

I am attempting to retrieve JSON data from the disk using a service: import { Product } from './../models/Product'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { HttpClient } from &apo ...

Attempting to revert the imported module back to its initial/default mock configuration

When working on my test file, I utilize a folder named mocks which contains various exported functions. Most of the time, I rely on the mocks folder to perform all necessary tasks. However, there is one scenario where I need to adjust the return value to a ...

Utilizing AWS Amplify with TypeScript and TypeScript Lambdas for powerful web development

Currently, I am working on a project that involves a nextjs frontend with TypeScript and AWS Amplify for the backend. My intention is to write my Lambda functions in TypeScript as well. However, I have encountered an issue where I have one tsconfig.json fi ...