Obtain a union type in TypeScript based on the values of a field within another union type

Is it feasible in Typescript to derive a union type from the values of a field within another union type?

type MyUnionType =
  | { foo: 'a', bar: 1 }
  | { foo: 'b', bar: 2 } 
  | { foo: 'c', bar: 3 }

// Is there an automatic way to achieve this? 
// Like creating a union of the possible values for foo in MyUnionType?
type Foos = 'a' | 'b' | 'c'

I had hoped Pick<MyUnionType, 'foo'> would work, but it doesn't quite deliver - it returns the desired type nested under a foo field: { foo: 'a' | 'b' | 'c' }

Answer №1

const Foos = MyUnionType['foo'] functions well when each type includes a foo field:

const MyUnionType = 
  | { foo: 'a', bar: 1 }
  | { foo: 'b', bar: 2 } 
  | { foo: 'c', bar: 3 }

const FooType = MyUnionType['foo']
// FooType = "a" | "b" | "c"

If you need to spread out over a diverse union type, you can narrow down to those types in the union with the field by doing this:

const SelectField<T, K extends string> = T extends Record<K, any> ? T[K] : never;

This allows you to utilize:

const MyUnionType = 
  | { foo: 'a', bar: 1 }
  | { foo: 'b', bar: 2 } 
  | { foo: 'c', bar: 3 }
  | { bar: 4 }

const FooType = MyUnionType['foo']
// Property 'foo' does not exist on type 'MyUnionType'. :-(

const FooType2 = PickField<MyUnionType, 'foo'>
// type FooType2 = "a" | "b" | "c"

const PickField<T, K extends string> = T extends Record<K, any> ? T[K] : never;
// Alternatively, you could return `undefined` instead of `never`
// in the false branch to capture the potentially missing data

Answer №2

A fantastic example of code from Sean Vieira. Here's an alternative approach, a bit more detailed:

const ALL_ITEMS =[
   { item: 'apple', quantity: 1 },
   { item: 'banana', quantity: 2 },
   { item: 'cherry', quantity: 3 }] as const

const ALL_ITEMS_LIST = [...ALL_ITEMS.map(i => i.item)] as const;

type MyProductType = typeof ALL_ITEMS;
type ItemType = typeof ALL_ITEMS_LIST;
// ItemType = "apple" | "banana" | "cherry"

Answer №3

If you want to access an entire set of potential data, like that found in an object literal type, follow these steps:

type Combine<T> =
    (T extends any ? (x: T) => any : never) extends
    (x: infer R) => any ? R : never

type ValueIntersectionByIndexUnion<T, TKey extends keyof Combine<T> = keyof Combine<T>> = T extends Record<TKey, any> ? ({
    [P in TKey]: T extends Record<P, any> ? (k: T[P]) => void : never
}[TKey] extends ((k: infer I) => void) ? I : never) : never;

type Example = { [K in keyof Combine<TA1>]: ValueIntersectionByIndexUnion<TA1, K> };

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

How to Create a Dependency on Tabs for Selecting Items in an Angular 7 Materials Dropdown List

I am currently working with angular 7 in combination with angular materials. In my project, I have implemented a tab as well as a selection list. What I aim to achieve is that the items displayed in the selection list are dependent on the chosen tab in th ...

Can the return type of a function be utilized as one of its argument types?

When attempting the following code: function chain<A, B extends (input: A, loop: (input: A) => C) => any, C extends ReturnType<B>> (input: A, handler: B): C { const loop = (input: A): C => handler(input, loop); return loop(input) ...

The error message "result.subscribe is not a function" indicates that there was a problem

I encountered an issue with the following error message: Uncaught TypeError: result.subscribe is not a function Here's a screenshot of the error for reference: Despite attempting to handle the error, I'm still struggling. Below is the snippet o ...

Encountering a challenge with triggering a dialog box from an onClick event on a pie chart in Angular 8 when utilizing chart.js

I am currently using a chart.js pie chart to showcase some data. I have managed to display the required information in an alert box when a slice of the pie is clicked. However, I am now looking for a way to present this data in a dialog box instead. &a ...

Adjusting the array when items in the multi-select dropdown are changed (selected or unselected)

I am looking to create a multi-select dropdown in Angular where the selected values are displayed as chip tags. Users should be able to unselect a value by clicking on the 'X' sign next to the chip tag, removing it from the selection. <searcha ...

Tips for managing the error message "The key 'myOptionalKey' is optional in the 'myObject' type but necessary in the '{...}' type"

Issue I'm currently working on making a sortable table using a sample table component from Material-UI. I encountered an error when I included an optional key in the Data object. It seems that the type definition in the getComparator function does no ...

React App Creation: Issue with ESLint configuration in TypeScript environment

I recently built a React app with the CRA (typescript template), but I noticed that TypeScript isn't adhering to the rules specified in the ESLint configuration. This is puzzling because I have consistently used this configuration in all my React proj ...

What is the best way to showcase information in Angular?

I am facing an issue where my cards are not displaying data from the database, they appear empty. Can anyone provide a solution to help me fix this problem? Below is the output I am getting, with the empty cards that I want to populate with data. MY TS: l ...

The Type '{Property: string, Property2: string} does not match the type Observable<Filters[]>

Here is a method I am trying to use: getFilters(): Observable<Filters[]> { let filters: Observable<Filters[]> = [ { property: "Value", property2: "Value2" }, { property: "Value3", property2: "V ...

Enhance your TypeScript React development with NeoVim

As a newcomer to vim, I decided to test my configuration with react and typescript. To do this, I created a simple demo app using the command npx create-react-app demo --template typescript. Next, I opened the directory in neovim by running nvim .. However ...

What techniques can be used to determine which exact key was matched by a generic?

I am trying to find a method to deduce a more general string type key from a specific string that can be associated with it. type Foo = { [x: `/tea/${string}/cup`]: void; [x: `/coffee/${string}/time`]: void; [x: `/cake/${string}/tin`]: void; } type ...

Help needed: Encountered an error stating "Module not found: Error can't resolve 'child_process', any solutions to resolve this issue?

I'm currently in the process of developing a JupyterLab extension using TypeScript. After successfully incorporating the package "@types/node" to access functionalities like 'require('http')', I encountered an issue when attemptin ...

A step-by-step guide on effectively adopting the strategy design pattern

Seeking guidance on the implementation of the strategy design pattern to ensure correctness. Consider a scenario where the class FormBuilder employs strategies from the following list in order to construct the form: SimpleFormStrategy ExtendedFormStrate ...

What is the best way to declare an array of objects within another array using typescript?

If you need to create an array of objects, the syntax would be: public racks: Rack[]; However, I am looking to create an array that can hold multiple arrays of racks, like this: [ [rack1, rack2, rack3], [rack4, rack5, rack6], [rack7] ] How ca ...

Using Material UI with React and TypeScript

I need some assistance with organizing my menus correctly in React using TypeScript. Currently, they are displaying on top of each other instead of within their respective category menus. I have been struggling to find a solution and would appreciate any h ...

Embed a dynamically generated SVG into an element in Angular without triggering any security warnings

Currently, in my angular 10 application, I am utilizing a library called svg.js to generate an SVG within the client. However, the specific library I am using is irrelevant for the question at hand. let svg = SVG().size(this.widthpx, this.heightpx).svg ...

The type of 'username' cannot be determined without specifying the reference to '@angular/forms/forms' in the node modules

I'm encountering an issue with my application: forgot-password.component.ts(44,7): error TS2742: The inferred type of 'username' cannot be named without a reference to '.../node_modules/@angular/forms/forms'. This is likely not po ...

The HTML button triggers a function to execute on a different webpage when clicked

I'm facing a straightforward issue that I can't seem to figure out due to my limited experience with Angular and web development. The problem revolves around two components, namely home and dashboard. In the home.component.html file, there's ...

What is the method for locating an element within an array?

The content being returned is presenting a challenge. How can I retrieve data from inside 0? I attempted to access it using date[0] without success const { data } = getData(); The result of console.log(data) is shown below: enter image description here ...

How is it possible to leverage the CDN URL mechanism for package management when working with Typescript?

Is it possible to use a CDN URL pointing mechanism to resolve packages in Typescript? One example is the Material Icon package installation, which can be included using the following code: <link rel="stylesheet" href="https://fonts.googleapis.com/icon? ...