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

Is there a way to seamlessly transition between different Angular components without having to refresh the entire webpage?

I'm currently working on implementing a navigation bar that allows users to switch between three components without having the navbar reload. The goal is for only the new component to load when the user clicks on a different section of the navbar, kee ...

Formatting decimals with dots in Angular using the decimal pipe

When using the Angular(4) decimal pipe, I noticed that dots are shown with numbers that have more than 4 digits. However, when the number has exactly 4 digits, the dot is not displayed. For example: <td>USD {{amount| number: '1.2-2'}} < ...

Troubleshooting: HTTP client post request working with body.set but not with formData.append for sending data to API

I am in the process of updating the UX for an older application with APIs developed in ASP.NET When I make a POST request as shown below, everything works perfectly. The data is received: var APIURL = sessionStorage.getItem('endpoint') + "/ ...

I encountered a permission denied error while attempting to execute the command npm install -g tsc

My main objective is to convert TypeScript code to JavaScript. However, when I attempted to install the TypeScript compiler globally using 'npm install -g tsc', I encountered the following error: npm ERR! Error: EACCES: permission denied, rename ...

Understanding the complexity of defining the type of a function can be challenging. If you're tasked with a complicated function, determining its type

Let's break it down: Dict is defined as { [key: string]: () => any } The desired return value is represented by X I am attempting to create a type for a function that: Takes in a dictionary Dict T Returns an X Now, X also functions as a functio ...

Automatic type inference for functions in TypeScript with arguments

I am looking to define an interface with the following structure: interface CheckNActSetup<D, C> { defs: (event: Event) => D, context: (defs: D) => C; exec: (context: C) => any[]; when: ((context: C) => boolean)[]; } and implement it usi ...

What are the steps to incorporate a 3D scene into a React website?

Can I get some advice on how to create a React web application using TypeScript? I want to be able to click a button and have it show a new page with a scene of a town. What is the best way to achieve this in my React project? I've heard about using R ...

I'm struggling to find the right Typescript syntax for defining a thunk function that returns a value while using React Redux Toolkit

Currently, I am utilizing TypeScript within a React Redux Toolkit project. While attempting to create an Async Thunk action function that is expected to return a boolean value, I found myself struggling with determining the correct TypeScript syntax: expor ...

Using Typescript, a vuex getter that includes an argument can be implemented to

Have you ever wondered how to create a Vuex store getter that takes a parameter argument? Check out this example: https://vuex.vuejs.org/en/getters.html I'm currently working on a project using Typescript (https://github.com/hmexx/vue_typescript_star ...

Removing redundant names from an array using Typescript

My task involves retrieving a list of names from an API, but there are many duplicates that need to be filtered out. However, when I attempt to execute the removeDuplicateNames function, it simply returns an empty array. const axios = require('axios&a ...

Using href with IconButtonProps is not supported

I'm facing a challenge in creating a wrapper for the IconButton. I aim to pass components or href props, but unfortunately, I am unable to achieve this by passing the IconButtonProps. Is there a way to accomplish this? function CustomIconButton(props ...

When using EcmaScript imports with the 'node16' or 'nodenext' module resolution, it is important to include explicit file extensions in relative import paths. For example, did you intend to use './*.js'?

Within my package.json file, I have set "type": "module" and utilize SWC for compiling TypeScript code. For imports, I utilize import Example from './example'. In addition, I use the following script: "start": " ...

Adjusting the settimeout delay time during its execution

Is there a way to adjust the setTimeout delay time while it is already running? I tried using debounceTime() as an alternative, but I would like to modify the existing delay time instead of creating a new one. In the code snippet provided, the delay is se ...

What is the best way to eliminate any extra spaces from a string using typescript?

Currently in my Angular 5 project, I am encountering an issue with using the .trim() function in TypeScript on a string. Despite implementing it as shown below, no whitespace is being removed and also there are no error messages appearing: this.maintabinf ...

Tips on typing a collection that may hold numerous instances of a particular object

When working with NgRx actions, I need to define the parameter. This parameter is an object that can contain a varying number of specific objects. These objects are already defined in an Interface. export interface CustomDistribution { maxWindowsActive ...

Tips on updating x-label formatting (to display months in words) utilizing morris.js with Angular 5

Snapshot of my Request. I'm looking to alter the xLabel (2018-01 - to - Jan) and I am utilizing morris.js. ...

Enhance user information by adding necessary fields

I often encounter situations where I need to select a specific entry from a set of data in a component, whether through a select box or as part of a table. However, the way I intend to utilize this data typically requires additional fields like the "label ...

Can you explain the significance of this code snippet 'true <=> false'?

Today I came across this piece of code: true <=> false. I'm a bit confused by it and don't really understand how it works. If anyone could shed some light on this expression for me, I would greatly appreciate it. For reference, this code ...

Insufficient attributes in TypeScript component for React application

Developing with React import {Input} from '@xxx/forms'; <Input label="account Name" name="account"/> Type Definition for input import React, { Ref } from 'react'; import { InputProps as UITKInputProps } from ...

Transforming a mongodb operation into an asynchronous function using await and async syntax

After calling the function to retrieve data from MongoDB, an undefined error occurs. It is suspected that converting the function to an async/await function may resolve this issue. However, there is uncertainty on how to make this conversion without disrup ...