Unraveling a discriminated union

I'm currently working on writing code that can handle generic discriminated unions with a type property.

Imagine I have multiple discriminated unions defined as follows:

interface IFoo {
  type: "foo";
  foo: number;
}

interface IBar {
  type: "bar";
  bar: number;
}

interface IBaz {
  type: "baz";
  baz: number;
}

type IObject = IFoo | IBar | IBaz;

The first challenge I tackled was identifying the possible values of the type property:

declare let _object: IObject;
type ObjectType = typeof _object.type;

(by the way, is there a way to do this without using an additional declare statement?)

Now, I need to define a generic type that can be used like so:

Case<IObject, "foo"> // = IFoo
Case<IObject, "bar"> // = IBar

This will allow me to create the following function:

function filter<Type extends ObjectType>(
    objects: IObject[], 
    type: Type,
): Case<IObject, type>[] {
  return objects.filter((o) => o.type == type);
}

Do you think this approach is feasible?

Answer №1

Absolutely, it is achievable

interface IFoo {
  kind: "foo";
  fooValue: number;
}

interface IBar {
  kind: "bar";
  barValue: number;
}

interface IBaz {
  kind: "baz";
  bazValue: number;
}

type ItemObject = IFoo | IBar | IBaz;

type TypeSelector<K extends string, T extends { kind: K }> =
       { [key in K]: T extends { kind: key } ? T : never };

type ChooseType<T extends { kind: string }, K extends T['kind']> =
       TypeSelector<T['kind'], T>[K];

type ResultFoo = ChooseType<ItemObject, "foo">; // = IFoo
type ResultBar = ChooseType<ItemObject, "bar">; // = IBar

You can also reference a type of a property using the 'indexed type query' type operator (using square brackets [] syntax)

type ItemType = ItemObject['kind'];

Finally, applying all this to a filter function provides the expected type for filtered array elements:

function filterItems<Kind extends ItemType>(
    items: ItemObject[], 
    chosenKind: Kind,
): ChooseType<ItemObject, Kind>[] {
  return items.filter((item) => item.kind == chosenKind);
}


let objects: ItemObject[];
const filteredResults = filterItems(objects, 'bar'); // inferred as const filteredResults: IBar[]

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

TypeScript - create an Interface that must have either the error field or the payload field, but

My Action type has the following requirements: MUST have a field type {String} CAN include a field payload {Object<string, any>} CAN include a field error {Error} Constraints: IF it contains the field payload THEN it cannot contain the field er ...

The process of extracting a value from an array of objects encountered an error due to the undefined object

I am looking to extract the value from an array within an object while also implementing error checking. The code I currently have checks if a specific key exists in the object and if the value associated with that key is of type array. If both condition ...

React: Updating a property in an array of objects causes properties to become undefined

My intention was simply to update a property within an object inside an array and then update the state of the array. However, I encountered an issue where all properties except the one that was updated became undefined. The code in question is as follows ...

(NextAuth) Error: The property 'session' is not found within the existing type '{}'

While working on a NextJs project with NextAuth, I encountered the following error: "Type error: Property 'session' does not exist on type '{}'.". To resolve this issue, I added the session property to my _app.tsx file as sugg ...

Expanding Material UI functionality across various packages within a monorepository

Currently, I am using lerna to develop multiple UI packages. In my project, I am enhancing @material-ui/styles within package a by incorporating additional palette and typography definitions. Although I have successfully integrated the new types in packag ...

The utilization of rxjs' isStopped function is now considered

We currently have this method implemented in our codebase: private createChart(dataset: any): any { if (!this.unsubscribeAll.isStopped) { this.chart = this.miStockChartService.createChart(dataset, this.chartId, this.options, this.extend ...

Angular 5 is throwing an error that says: "There is a TypeError and it cannot read the property 'nativeElement' because it

Being aware that I may not be the first to inquire about this issue, I find myself working on an Angular 5 application where I need to programmatically open an accordion. Everything seems to function as expected in stackblitz, but unfortunately, I am enco ...

Upon refreshing the page, the Vuex store getter displays [__ob__: Observer]

After each page refresh, my Vuex store getter returns [__ob__: Observer]. The version of Vue being used is 2.2.3 along with TypeScript. Initially, the data loads correctly from an external API, but when utilizing the getter, it fails and requires another ...

The function useNuxtApp() in Nuxt 3 is returning an unknown type

I have been working on creating a helper that can be used across all composables and applications in my Nuxt plugin. Here is how the code looks: // hello.ts export default defineNuxtPlugin(async nuxtApp => { nuxtApp.vueApp.provide('hello', ...

Angular error: Attempting to access the value property of an undefined object

When attempting to delete a row from a table, an error occurred stating "TypeError: Cannot read property 'value' of undefined" after placing the delete button at the end of a row. I watched this video tutorial for guidance on deleting a row witho ...

Are you interested in creating dynamic tables/models with Sequelize?

Currently, I am exploring a theoretical question before diving into the implementation phase. The scenario is as follows: In my application, users have the ability to upload structured data such as Excel, CSV files, and more. Based on specific requirement ...

The service has terminated unexpectedly because of signal: Ended prematurely: 9

I'm encountering the error 'Service exited due to signal: Killed: 9' and am unable to launch my app. I've come across information suggesting that this may be caused by memory leaks or a lengthy startup time for the app. In all honesty, ...

PlayWright - Extracting the text from the <dd> element within a <div> container

Here is the structure I am working with: <div class="aClassName"> <dl> <dt>Employee Name</dt> <dd data-testid="employee1">Sam</dd> </dl> </div> I am attempting to retrie ...

Challenge Encountered with Create-React-App TypeScript Template: Generating JS Files Instead of TSX Files

Encountering a problem setting up a new React application with TypeScript using the Create-React-App template. Followed the guidelines on the official documentation (https://create-react-app.dev/docs/adding-typescript/) and ran the command below: npx creat ...

Transforming a function into an array in TypeScript

I attempted to use the map() function on a dataURL array obtained from the usePersonList() hook, but I am struggling to convert my function to an array in order to avoid errors when clicking a button. import Axios from "axios"; import React, { us ...

Verify the completeness of data types within an array in typescript

I am currently developing a comprehensive match function that I want to ensure is exhaustive during compile time. Although adding a default case would help with this, I am intrigued by some interesting dependent typing techniques I have come across. It wou ...

Steps for importing jQuery typings into TypeScript:1. First, install the jQuery

I've searched for similar questions, but haven't found one that matches my issue. Can someone help me figure out what to do next? In my Visual Studio project, I used package.json to download jquery typings into the node_modules folder: { "ver ...

React Native Component Error: Cannot read property '_this' of undefined

I am currently developing a face recognition application using React Native 0.63. I'm running my project using react-native run-android. However, I encountered an issue with Component Exception where it shows 'undefined is not an object (evaluati ...

Identifying whether a particular area is represented in a geographic map array presents a significant challenge

Having an issue with typescript currently. I have a variable that contains different string entries representing x, y positions. The entries are as follows: ["3,3","3,4","3,5","2,3","2,4","2,5","-1,-2","-2,- 2","-2,-1"] My goal is to determine if this land ...

Potential absence of value in this Vue 3 component's 'this' placement

I've been encountering an issue with using this.$refs within my Vue component. No matter where I place it - whether in methods, lambdas, or lifecycle hooks - I consistently receive errors indicating that 'this' may be undefined. As a newcome ...