Developing a discriminated union by utilizing the attribute names from a different type

In my quest to create a unique generic type, I am experimenting with extracting property names and types from a given type to create a discriminated union type. Take for example:

type FooBar = {
    foo: string;
    bar: number;
};

This would translate to:

type FooBarPair = {
    key: "foo",
    value: string
} | {
    key: "bar",
    value: number
}

My initial approach looks like this:

type Pairs<T> = {
    [TKey in keyof T]: {
        key: TKey;
        value: T[TKey];
    };
};

type Pair<T> = Pairs<T>[keyof T];

However, when testing it with the aforementioned type:

let pair: Pair<FooBar> = {
    key: "foo",
    value: 3
};

I expected a compilation error but surprisingly there wasn't one. It turns out that the type of Pair<FooBar> boils down to:

{
    key: "foo" | "bar";
    value: string | number;
}

While I suspect this could be a bug within TypeScript, I'm open to exploring alternative approaches to achieve the desired outcome.

Answer №1

There is actually a way to accomplish this task. It seems that Typescript automatically deduces the desired type

 `{ key: "foo"; value: string; } | { key: "bar"; value: number; }`

if you eliminate the intermediate generic type

type Pair<T> = Pairs<T>[keyof T];
and instead expand it in-line such as
Pairs<FooBar>[keyof FooBar]
whenever needed:

type Pairs<T> = {
    [TKey in keyof T]: {
        key: TKey;
        value: T[TKey];
    };
};

type FooBar = {
    foo: string;
    bar: number;
};


let pair: Pairs<FooBar>[keyof FooBar] = {
    key: "foo",
    value: 3
};

The detailed error message states:

Type '{ key: "foo"; value: number; }' is not assignable to type '{ key: "foo"; value: string; } | { key: "bar"; value: number; }'.
  Type '{ key: "foo"; value: number; }' is not assignable to type '{ key: "foo"; value: string; }'.
    Types of property 'value' are incompatible.
      Type 'number' is not assignable to type 'string'.

I'm uncertain about the "correct" inference for the type in either scenario, but introducing a shorthand like

Pair<T> = Pairs<T>[keyof T]
should ideally have no impact on type inference. This appears to be a potential bug.

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

Utilizing a segment of one interface within another interface is the most effective method

In my current project using nextjs and typescript, I have defined two interfaces as shown below: export interface IAccordion { accordionItems: { id: string | number; title: string | React.ReactElement; content: string | React. ...

Ensure that the Promise is resolved upon the event firing, without the need for multiple event

I'm currently working on a solution where I need to handle promise resolution when an EventEmitter event occurs. In the function containing this logic, an argument is passed and added to a stack. Later, items are processed from the stack with differe ...

How can I change an icon and switch themes using onClick in react js?

I have successfully implemented an icon click feature to change the colorscheme of my website (in line 21 and changeTheme). However, I also want the icon to toggle between FaRegMoon and FaRegSun when clicked (switching from FaRegMoon to FaRegSun and vice v ...

Adjusting various angular-cli configuration files or providing input variables

My application caters to different customers, requiring personalized configurations based on their needs. I am looking for a way to customize the settings in the angular-cli.json file each time I run ng build. Is there a method to: 1) Dynamically cha ...

Encountering an issue while trying to integrate custom commands using the addCommand function in WebDriverIO

When using the addCommand function to add a new command, I encountered an error message that states: [ts] Property 'WaitForElementsAmount' does not exist on type 'Client<void>'. Here is an example of the code snippet I used: br ...

Using Moment JS to display the days of the upcoming week

I'm in the process of developing a weather application and I need to create code that will display the upcoming week's weather forecast. The only information I have from the server is a "time" entity with a "value" set for next Monday such as "20 ...

What causes the discrepancy in values displayed by enums in TypeScript when assigned integers in reverse order?

Recently diving into the world of TypeScript, I've been experimenting with different types in this language. One interesting data type I played with is enums. Here's an example of code I used: enum colors {red=1,green=0,blue,white}; console.lo ...

Is it true that "Conditional types" are not compatible with actual functions?

Checking out https://www.typescriptlang.org/docs/handbook/2/conditional-types.html I'm curious as to why this code is not functioning properly? interface IdLabel { id: number } interface NameLabel { name: string } type NameOrId<T extends num ...

Within an Angular test scenario, execute a static method from a service that triggers an HTTP get request to fetch stored JSON data. This data is then retrieved and returned back to the service

Currently, I am facing a challenge in my Angular test case where I am trying to load JSON data via an HTTP call. The issue arises when a static method is called from a service spec file named "url-service.spec" to another service named "load-json.service. ...

Guide on dividing a URL string in Angular framework

Is there a way to include a value directly in the URL, like so: http://example.com/component/july2021 I need to extract july2021 from the component and separate it into "july" and "2021". How can I achieve this? ...

Please ensure the subscription has completed before proceeding with the loop

I am currently working on an Angular application that retrieves data from an API and uses one of its parameters from a looped array. The issue I'm facing is that the data is pushed in a random order due to the continuous looping without waiting for th ...

Steps for sending a POST request for every file in the given array

I am working on an angular component that contains an array of drag'n'dropped files. My goal is to make a POST request to http://some.url for each file in the array. Here is what I have been attempting: drop.component.ts public drop(event) { ...

Is there a way to customize the Color Palette in Material UI using Typescript?

As a newcomer to react and typescript, I am exploring ways to expand the color palette within a global theme. Within my themeContainer.tsx file, import { ThemeOptions } from '@material-ui/core/styles/createMuiTheme'; declare module '@mate ...

Updating a one-to-one relationship in TypeORM with Node.js and TypeScript can be achieved by following these steps

I am working with two entities, one is called Filter and the other is Dataset. They have a one-to-one relationship. I need help in updating the Filter entity based on Dataset using Repository with promises. The code is written in a file named node.ts. Th ...

What is the reasoning behind ethers.js choosing to have the return value of a function be an array that contains the value, rather than just the value itself

An issue arose with the test case below: it('should access MAX_COUNT', async () => { const maxCount = await myContract.functions.MAX_COUNT(); expect(maxCount).to.equal(64); }); The test failed with this error message: ...

Retrieve selected button from loop typescript

https://i.stack.imgur.com/DS9jQ.jpgI have an array of individuals that I am looping through. It's a bit difficult for me to explain, but I want something like this: <div *ngFor="let person of persons"> {{person.name}} {{person.surname}} <but ...

Tips for setting up a proxy with an enum

I am facing an issue with setting up a Proxy for an enum. Specifically, I have an enum where I want to assign a value to this.status using a Proxy. However, despite my expectations, the output "I have been set" does not appear in the console. Can anyone ex ...

Challenge with React CloneElement regarding typing

Utilizing React Children and React Clone element, I aim to trigger methods in both the wrapper and Select components upon onClick event in the Option component. Although everything is functioning correctly, I am encountering a type error when calling the O ...

Is there a way for me to maintain a consistent layout across all pages while also changing the content component based on the URL route in Next.js?

I'm currently working with Typescript and Next.js My goal is to implement a unified <Layout> for all pages on my website. The layout comprises components such as <Header>, <Footer>, <Sidenav>, and <Content>. Here is the ...

Exploring ways to exclude a column in a TypeORM entity while also providing the flexibility to make it optional for retrieval using the Find method

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm"; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; } i prefer not to include the password here as I want it to be returned to the client: ...