What is the best way to sort a union based on the existence or non-existence of a specific

My API response comes in the form of a IResponse, which can have different variations based on a URL search parameter.

Here is how I plan to utilize it:

const data1 = await request<E<"aaa">>('/api/data/1?type=aaa');
const data2 = await request<E<"bbb">>('/api/data/1?type=bbb');

function request<T>(
  url: string
): Promise<T> {
  return fetch(url, config)
    .then((response) => response.json())
    .then((data) => data as T);
}

//index.ts

import {
  paths,
} from "./autoGeneratedTypes";

type IResponse =
  paths["/api/data/{id}"]["get"]["responses"]["200"]["content"]["application/json"];

export type E<T extends "aaa" | "bbb"> = T extends "aaa"
  ? Omit<IResponse, "ca">
  : Pick<IResponse, "ca">;

export type F = E<"aaa">["bb"];

Encountering an error at

Pick<IResponse, "ca">
stating
Type 'string' does not satisfy the constraint 'never'

To avoid directly referencing schemas C or B due to possible schema name changes, I require a type that will correctly extract the API response type from IResponse.

//autoGeneratedTypes.ts
/**
 * This file was auto-generated by openapi-typescript.
 * Do not make direct changes to the file.
 */

export interface paths {
  "/api/data/{id}": {
    /** @description get the data */
    get: {
      responses: {
        /** @description Retrieved the data successfully */
        200: {
          content: {
            "application/json": components["schemas"]["D"];
          };
        };
      };
    };
  };
}

export interface components {
  schemas: {
    D: components["schemas"]["C"] | components["schemas"]["B"];
    C: {
      ca: components["schemas"]["B"][];
    };
    B: {
      ba: string;
      bb: components["schemas"]["A"][];
    };
    A: {
      aa: string;
      ab: string | null;
    };
  };
}

Link to Typescript Playground

Answer №1

If you are dealing with union types in TypeScript, the Extract<T, U> and Exclude<T, U> utility types can be very helpful. These types allow you to filter out members of a union type based on certain criteria.

To achieve this, you can use something like:

type E<T extends "aaa" | "bbb"> =
  T extends "aaa" ? Exclude<IResponse, { ca: any }> : Extract<IResponse, { ca: any }>

which will give you the desired result.

It's worth noting that using Pick<T, K> and Omit<T, K> might not work well when dealing with union types, as they do not distribute over unions in TypeScript.

For a more interactive example, check out this Playground link.

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

Converting a JavaScript function to work in TypeScript: a step-by-step guide

When writing it like this, using the this keyword is not possible. LoadDrawing(drawing_name) { this.glg.LoadWidgetFromURL(drawing_name, null, this.LoadCB,drawing_name); } LoadCB(drawing, drawing_name) { if (drawing == null) { return; ...

Angular sets the required property only when the button is clicked

Is there a way to make a field required in Angular only when a button is clicked? Currently, the error message appears even before the user interacts with the field. I would like the error message "folder name is required" to only appear when the user cli ...

React: Avoid unnecessary re-rendering of child components caused by a bloated tree structure

I am dealing with a tree/directory structured data containing approximately 14k nodes. The issue I am facing is that every time a node is expanded or minimized by clicking a button, causing it to be added to an 'expanded' Set in the Redux state, ...

There was a problem with the module '@angular/material' as it was unable to export a certain member

In creating a custom Angular Material module, I have created a material.module.ts file and imported various Angular Material UI components as shown below: import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/commo ...

Exploring the usage of slots in WebComponents without relying on shadow DOM

I am working on developing a WebComponent without utilizing ShadowDOM. So far, everything has been going smoothly. However, I now aim to create a component that wraps other components similar to how it is done in Angular with @ViewChild / @ViewChildren. (I ...

Is it possible to modify the Angular global error handler to accept additional parameters besides just the error object?

After exploring the capabilities of https://angular.io/api/core/ErrorHandler, I have found that it allows me to override the global error handler with an error object. I appreciate how I can easily define my own global error handler and use it wherever nec ...

Using 3 dots argument in Angular 5 to assign values to an array

I stumbled upon this line of code in Angular. Can someone explain its meaning? this.columns = [...this.columns, col]; My guess is that this relates to the immutable concept of arrays. ...

When using Vue with Vuetify, be aware that object literals can only specify known properties. In this case, the type 'vuetify' does not exist in the ComponentOptions of Vue with DefaultData

Started a fresh Vue project with TypeScript by following this guide: https://v2.vuejs.org/v2/guide/typescript.html If you don't have it installed yet, install Vue CLI using: npm install --global @vue/cli Create a new project and choose the "Manual ...

The @Input() function is failing to display or fetch the latest value that was passed

I am currently working on an angular project, and I've encountered a situation where I'm attempting to send a value from a parent component to a child component using the @Input() decorator. Despite my efforts, the child component continues to di ...

A static method written in Typescript within an abstract class for generating a new instance of the class itself

Imagine I have abstract class Foo { } class Bar1 extends Foo { constructor(someVar) { ... } } class Bar2 extends Foo { constructor(someVar) { ... } } I want to create a static method that generates an instance of the final class (all construct ...

What is the best way to determine the property type dynamically in TypeScript based on the value of another property?

I have been working with Polymorphic relationships and currently have the following TypeScript interface defined: interface SubjectA {} interface SubjectB {} interface SubjectC {} enum SubjectType { SubjectA = 'Subject A', SubjectB = 'S ...

Leveraging non-ionic-native Cordova plugin

I'm currently working on integrating the [This]https://github.com/louisbl/cordova-plugin-locationservices plugin into my Ionic 4 app. Since it's a non-Ionic-native plugin, what is the proper way to call its functions within my TypeScript code? ...

Encountered an issue while attempting to assign a value to a property that is declared as [key in keyof T]

I am currently working on implementing a function that selects specific properties of an object. Here is the current function: const project = function<T>(object: T, projection: Projection<T>): Partial<T> { throw new Error("not imple ...

What are the best methods for testing a function containing multiple conditional statements?

I have a complex function that I want to showcase here, it's quite simple but for some reason, I'm struggling with writing unit tests for it. I don't need the exact unit test implementation, just a general approach or tips on how to handle i ...

Transforming time into luxon time frames and hours

Is there a way to convert this block of code from moment.js to luxon? Here is the code snippet for reference: The following code is written using moment.js, but I would like to achieve the same functionality using luxon. timezone: null, getIn: moment() ...

Certain Material-UI components appear to lack proper styling

I found a tutorial on how to incorporate material UI into my app at this link: https://mui.com/material-ui/getting-started However, I noticed that some components are not styled as expected and customizing the theme seems to have no effect... This is how ...

Unpacking and reassigning variables in Vue.js 3 using TypeScript

I am working with a component that has input parameters, and I am experimenting with using destructuring assignment on the properties object to reassign variables with different names: <script setup lang="ts"> const { modelValue: isSelected ...

Challenges with Type Casting in TypeScript

Upon reviewing a specific piece of code, I noticed that it is not producing any compile time or run time errors even though it should: message: string // this variable is of type string -- Line 1 <br> abc: somedatatype // lets assume abc is of some ...

Error: Loki cannot be used as a constructor

Can anyone assist me in understanding why this code is not functioning correctly? Here's what my index.ts file in Hapi.js looks like: import { Server, Request, ResponseToolkit } from '@hapi/hapi'; import * as Loki from 'lokijs'; ...

Error when compiling TypeScript: The callback function provided in Array.map is not callable

This is a Node.js API that has been written in Typescript. app.post('/photos/upload', upload.array('photos', 12), async (req, res) => { var response = { } var list = [] try { const col = await loadCollection(COLLECTION_NAM ...