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

The validation process in Redux forms

Imagine we have the following types defined: interface MyFormFields { FirstName: string; LastName: string; } type FieldsType = keyof MyFormFields; const field1: FieldsType = "c"; const field2 = "c" as FieldsType; Now, I am looking to implemen ...

What could be causing the TypeScript exhaustive switch check to malfunction?

How come the default case in the switch statement below does not result in an exhaustive check where 'item' is correctly identified as of type 'never'? enum Type { First, Second } interface ObjectWithType { type: Type; } c ...

Why does the Angular page not load on the first visit, but loads successfully on subsequent visits and opens without any issues?

I am currently in the process of converting a template to Angular that utilizes HTML, CSS, Bootstrap, JavaScript, and other similar technologies. Within the template, there is a loader function with a GIF animation embedded within it. Interestingly, upon ...

In what manner can I retrieve this particular type?

Can you provide me with an example of how to use this type? interface MyCode{ (): Function; title: string; } I've been thinking about various possibilities but haven't been able to figure it out. One approach is: let testCode: MyCode = ...

Is React Typescript compatible with Internet Explorer 11?

As I have multiple React applications functioning in Internet Explorer 11 (with polyfills), my intention is to incorporate TypeScript into my upcoming projects. Following the same technologies and concepts from my previous apps, I built my first one using ...

Troubleshooting problem with sorting in Angular 4 material header

Using Angular 4 material for a table has presented me with two issues: 1. When sorting a table, it displays the description of the sorting order in the header. I would like to remove this. It displays "Sorted by ascending order" here. The ngx modal theme ...

Using Phoenix Channels and Sockets in an Angular 2 application: A comprehensive guide

My backend is built with Elixir / Phoenix and my frontend is built with Angular 2 (Typescript, Brunch.io for building, ES6). I'm eager to start using Phoenix Channels but I'm struggling to integrate the Phoenix Javascript Client into my frontend. ...

React with TypeScript - Troubleshooting TS Error when Iterating over List Item (LI) Elements

Iterating through a group of <li> elements to strip away a specific class, then applying the same class to a different <li>. See the snippet below: useEffect(() => { if (dnArrowIdx === undefined) { const allLi = Array.from(document. ...

Integrating HTTP JSON responses into HTML using Ionic 2, Angular 2, TypeScript, and PHP: A comprehensive guide

Currently in the midst of developing my first Ionic 2 app, however, my understanding of typescript is still limited.. I aim to execute the authenticate() method within my constructor and then to: Retrieve the entire JSON response into the textarea and/o ...

Generate several invoices with just a single click using TypeScript

I'm interested in efficiently printing multiple custom HTML invoices with just one click, similar to this example: Although I attempted to achieve this functionality using the following method, it appears to be incorrect as it prompts the print dialo ...

Exploring Angular 4's Capabilities: Navigating a Multi-Dimensional Array

I am currently working with a multi-dimensional array that has two keys, and it is structured as follows: user: any = {}; // The index is incremented within a for loop to add values to the user object (this part is functioning correctly) this.user[index++ ...

What is the best way to organize information in a table based on the date

This is my data table https://i.stack.imgur.com/1DNlj.png in the displayed table, the registration dates are sorted with the oldest date at the top. However, I aim to have the newest data displayed first. Below is the code snippet I am using: this.dataSo ...

Is it possible to merge these two scripts into a single one within Vue?

As a newcomer to VUE, I am faced with the task of modifying some existing code. Here is my dilemma: Within a single component, there are two script tags present. One of them contains an import for useStore from the vuex library. The other script tag incl ...

Guide to forming an array by extracting specific properties from a nested JSON array using javascript

Currently, I have this list: list = { id: 1, arr: [ {index : 1 , description: "lol" , author: "Arthur"}, {index : 2 , description: "sdadsa" , author: "Bob"}, {index : 3 , desc ...

What is the method for including as: :json in your code?

I have a file with the extension .ts, which is part of a Ruby on Rails application. The code in this file looks something like this: export const create = async (params: CreateRequest): Promise<XYZ> => { const response = await request<XYZ> ...

Displaying svg files conditionally in a react native application

I have developed an app specifically for trading dogs. Each dog breed in my app is associated with its own unique svg file, which are all stored in the assets folder (approximately 150 svg files in total). When retrieving post data from the backend, I re ...

Tips for injecting a service into a class (not a component)

Can you inject a service into a class that is not a component? Consider the following scenario: Myservice import {Injectable} from '@angular/core'; @Injectable() export class myService { dosomething() { // implementation } } MyClass im ...

Using TypeScript, apply an event to every element within an array of elements through iteration

I have written the code snippet below, however I am encountering an issue where every element alerts the index of the last iteration. For instance, if there are 24 items in the elements array, each element will alert "Changed row 23" on change. I underst ...

Establishing a default value as undefined for a numeric data type in Typescript

I have a question regarding setting initial values and resetting number types in TypeScript. Initially, I had the following code snippet: interface FormPattern { id: string; name: string; email: string; age: number; } const AddUser = () => { ...

What is the process for building .ts components within a Vue application?

Looking to update an old project that currently uses the 'av-ts' plugin and vue-ts-loader to compile .ts files as Vue components. The project is running on Vue 2.4.2, but I want to upgrade it to version 2.6.14. However, since 'vue-ts-loader& ...