"Choose one specific type in Typescript, there are no in-b

Need help returning an object as a fetch response with either the property "data" or "mes":

{ data: Data } | { mes: ErrMessage }

Having trouble with TypeScript complaining about this object, let's call it props:

if (prop.mes) return // Property 'mes' does not exist on type '{ data: string; }'.
prop.data // Property 'data' does not exist on type '{ mes: string; }'.

Is there a better alternative than defining a large type in the component each time?

{ data: Data, mes: false } | { mes: ErrMessage, data: false}

Check out the example on Typescript playground here.

type ErrMessage = string // some complex structure

const fetchAPI = <Body = any, Data = any>(link: string, body: Body): Promise<{ data: Data } | { mes: ErrMessage }> => {
  return new Promise((resolve) => {
      fetch(link, { body: JSON.stringify(body) })
      .then((raw) => raw.json())
      .then((res) => {
        resolve(res.err ? { mes: res.err } : { data: res })
      })
      .catch((err) => {
        resolve({ mes: 'text' })
      })
  })
}

type Data = string // some complex structure
type MyReq = string // some complex structure
type MyRes = {data: Data } | {mes: ErrMessage}

const someLoader = async () => {
    const res = await fetchAPI<MyReq, MyRes>('reader', 'body')
    return {res}
}

const componentThatGetProp = (prop: MyRes ) => {
    // error handler
    if (prop.mes) return // Property 'mes' does not exist on type '{ data: string; }'.

    // do something
    prop.data // Property 'data' does not exist on type '{ mes: string; }'. 
}

Answer №1

Rather than using

if (prop.mes) return

consider using

if ('mes' in prop) return

One method attempts to retrieve the property (which may not exist), while the other method verifies its presence

Answer №2

In TypeScript, the ideal type to use is as follows:

type MyRes = { data: Data, mes?: undefined} | { mes: ErrMessage, data?: undefined}

The key point here is that the irrelevant properties in each union member should be optional and set to undefined, rather than false. With this setup, if you check an irrelevant property, it will simply be missing. Since TypeScript 3.2, the compiler recognizes this type as a discriminated union, resolving any potential errors:

const componentThatGetProp = (prop: MyRes) => {
  // error handler
  if (prop.mes) return // valid
  // perform actions
  prop.data // valid
}

Converting an existing union type like MyRes manually into one where each member includes all keys can be tedious. Fortunately, TypeScript's conditional types allow you to programmatically transform a regular union into the discriminated format described above. Here is an example using a type alias called ExclusifyUnion<T>:

type AllKeys<T> = T extends any ? keyof T : never;

type ExclusifyUnion<T, K extends AllKeys<T> = AllKeys<T>> =
  T extends any ?
  (T & { [P in Exclude<K, keyof T>]?: never }) extends infer U ?
  { [Q in keyof U]: U[Q] } :
  never : never;

This code snippet dynamically converts a union type into the desired discriminated form. For instance, applying it to MyRes produces the following result:

type MyRes = ExclusifyUnion<{ data: Data } | { mes: ErrMessage }>
/*type MyRes = {
    data: string;
    mes?: undefined;
} | {
    mes: string;
    data?: undefined;
}*/

This approach is scalable even when dealing with more complex union types.


I hope this provides you with a clear direction moving forward. Best of luck!

Playground link for code demo

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

Exploring the DynamoDB List Data Type

Currently, I am working on an angular 8 application where I have chosen to store JSON data in a list data type within DynamoDB. Inserting records and querying the table for data has been smooth sailing so far. However, I have run into some challenges when ...

The module declaration is triggering a lint error that reads "Unexpected token, expecting '{'"

To address the absence of available types for a library, I created the file omnivore.d.ts in TypeScript. Within this file, I added the following line: declare module '@mapbox/leaflet-omnivore' Upon running vue-cli-service lint in my project, an ...

Typescript: When using ts-node-dev, an error occurred while trying to import express due to an unexpected

I am embarking on a fresh project using Typescript and I intend to set up the node server with typescript utilizing express. There's a helpful tutorial that explains how to execute a Typescript file without going through the hassle of compiling files, ...

To set up the store in configureStore, you must provide one type argument for the 'MakeStore' generic type

Encountering an issue with MakeStore showing a Generic type error 'MakeStore' requires 1 type argument(s) .ts(2314) Here is the code from configureStore.ts: import { configureStore, EnhancedStore, getDefaultMiddleware, } from '@reduxj ...

Angular 2 component downgrade issue: What causes the error when constructor parameters are involved? (SystemJS) Unable to resolve all parameters for (?)

Consider this line as an example: constructor(private elementRef: ElementRef, private zone: NgZone) {} In order for the downgrade to be successful without any errors, I must remove the parameters from the constructor. Otherwise, I encounter the follo ...

Tips on preventing the need for null or undefined checks in JS/Typescript singletons that have an initialization function

Is there a way to streamline the process of handling props in an Object literal that is dynamically initialized only once? I'm looking for a pattern that would eliminate the need for repetitive null/undefined checks and throw errors when certain metho ...

I need guidance on retrieving items from the useRouter() hook when utilizing Next.js with the App Router approach

This code snippet demonstrates how to use Pages Router in React import { useRouter } from "next/router"; import React from "react"; function ShowroomListings({}) { const router = useRouter(); const { listingId } = router.query as ...

Discovering the interface type of class properties in order to implement a factory method

I am struggling with implementing a factory method in my code. I want to be able to pass not only a Class type to instantiate but also a set of default values for the properties within the class. My goal is to have the compiler notify me if I try to pass i ...

Angular2: Exploring the Differences Between Observable.fromEvent and Button Click

As I work with my code, I have noticed that I am using both <button (click)="callsomefucntion" /> and Observable.fromEvent<MouseEvent>(button.nativeElement.'click') interchangeably. I am curious to understand the distinction between ...

Tips for displaying a multi-select dropdown in the Creative Tim Angular Material Pro filter panel

I am in need of assistance with modifying the standard Creative Tim Angular Pro Material template due to my limited CSS/SCSS skills. Could someone provide examples of the necessary changes, whether it involves altering the HTML or multiple CSS files withi ...

Altering the properties of a specified element within TestBed using the overrideComponent method

We are implementing TestBed.overrideComponent() to substitute a component with custom behavior. TestBed.overrideComponent(CoolComponent, { set: { template: '<div id="fake-component">i am the fake component</div>', sel ...

What is the reason that control flow analysis does not support the never type?

Here is the scenario I'm dealing with (utilizing strictNullChecks): function neverReturns(): never { throw new Error(); } const maybeString: string | null = Math.random() > 0.5 ? "hi" : null; if (!maybeString) { neverReturns(); // th ...

Exploring Sequelize: Uncovering the Secret to Retrieving Multiple Associated Items of Identical Type

Within my database, I have a situation where there are two tables sharing relations of the same type. These tables are named UserCollection and ImagenProcess UserCollection has two instances that relate to ImagenProcess. Although the IDs appear unique whe ...

What is the best way to provide inputs to a personalized validation function?

I am seeking a solution to pass an array of prefix strings to my custom validator in order to validate that the value begins with one of the specified prefixes. Below is the code snippet for my current validator: @ValidatorConstraint({ name: 'prefixVa ...

What could be causing the primeng dialog to appear blank when conducting Jasmine tests on this Angular TypeScript application?

Having trouble testing a component due to rendering issues? Check out the code snippet below: import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@angular/core'; @Component({ selector: 'app-help', cha ...

Utilizing TypeScript 3.1: Easier Array Indexing with Enums in Strict Mode

Enabling TypeScript "strict" mode with "noImplicitAny" causes this code to fail compilation. I am looking for guidance on how to properly declare and use Arrays indexed by Enum values. namespace CommandLineParser { enum States { sNoWhere, sSwitchValu ...

I'm struggling to get a specific tutorial to work for my application. Can anyone advise me on how to map a general URL to the HTTP methods of an API endpoint that is written in C

I am struggling to retrieve and display data from a C# Web API using Typescript and Angular. As someone new to Typescript, I followed a tutorial to create a service based on this guide: [https://offering.solutions/blog/articles/2016/02/01/consuming-a-rest- ...

Tips for resolving relative child routes in Angular

Routing Configuration const routes: Routes = [ { path: '', loadChildren: './home/home.module#HomeModule' }, { path: 'admin', loadChildren: './admin/admin.module#AdminModule' } ]; Nested Home Routing const ro ...

Tips for continuously running a loop function until retrieving a value from an API within a cypress project

Need help looping a function to retrieve the value from an API in my Cypress project. The goal is to call the API multiple times until we receive the desired value. let otpValue = ''; const loopFunc = () => { cy.request({ method: &ap ...

Error in Angular 13: Struggling to remove the likelihood of an object being null

I am working on a piece of code that includes the following: var item = document.getElementById("div0"); item.parentNode.removeChild(item); // The error seems to be here Every time I run this code, I encounter the error message: object is p ...