Retrieve a specific subset of a union based on the class in a generic function

This question shares similarities with another post I made, but this time focusing on using classes instead of plain objects.

class Exception1 extends Error {
    constructor(message: string, public arg1: string) {
        super(message);
    }
}

class Exception2 extends Error {
    constructor(message: string, public arg2: string) {
        super(message);
    }
}

type MyException = Exception1 | Exception2;

type Constructor<T> = new (...args: any) => T;

function filterExceptions<E extends MyException>(exceptions: MyException[], classes?: Constructor<E>[]): E[] {
  if (!classes) return exceptions as E[];
  return exceptions.filter((exception) => classes.some((klass) => exception instanceof klass)) as E[];
}

const ex1 = new Exception1("ex1", "whatever");
const ex2 = new Exception2("ex2", "whatever");
const exceptions = [ex1, ex2]
const f1 = filterExceptions(exceptions); // OK
const f2 = filterExceptions(exceptions, [Exception1]); // OK
const f3 = filterExceptions(exceptions, [Exception1, Exception2]) // Error

Also accessible at Typescript playground.

The error message states:

Types of construct signatures are incompatible.
  Type 'new (message: string, arg2: string) => Exception2' is not assignable to type 'new (...args: any) => Exception1'.
    Property 'arg1' is missing in type 'Exception2' but required in type 'Exception1'.(2419)

I found a temporary solution by specifying the generic parameter during the function call:

const f3 = filterExceptions<Exception1 | Exception2>(exceptions, [Exception1, Exception2])

However, this is not an ideal approach.

PS. Removing extra constructor arguments in Exception1 and Exception2 eliminates the error, but causes f3 to be typed as Exception1[] instead of (Exception1 | Exception2)[]

Answer №1

Now it’s my turn to step in for my alternate account and provide further details on my proposed solution.

In all honesty, I prefer working with constructors rather than instance types because InstanceType is built-in while Constructor is not, making the answer more straightforward and concise.

type MyError = InstanceType<MyErrorConstructor>;
type MyErrorConstructor = typeof Error1 | typeof Error2;

As shown above, I have united all error constructors followed by a union of instance types.

The next step involves modifying the definition of filterErrors:

function filterErrors<
  Constructors extends MyErrorConstructor = MyErrorConstructor
>(errors: MyError[], classes?: Constructors[]): InstanceType<Constructors>[] {

The parameter errors can contain any list of errors, meeting our requirements. The optional parameter classes, if specified, refines the return type of the function. Otherwise, it defaults to MyErrorConstructor.

You can now effortlessly call this function without encountering any issues:

const f1 = filterErrors(errors);                   // (Error1 | Error2)[]
const f2 = filterErrors(errors, [Error1]);         // Error1[]
const f3 = filterErrors(errors, [Error1, Error2]); // (Error1 | Error2)[] 

Unfortunately, you may notice the somewhat unsightly casts that I’ve utilized. For now, I haven’t come across a cleaner alternative that does not require some form of casting.

https://www.typescriptlang.org/play?#code/MYGwhgzhAECiBO8D28CM0CmAPALhgdgCYwLLzQDeAsAFDT3TBL4Q7wCuwOKAFALYYoYAOYYAXNFbwAlvmEAaaAAd2AIxDTg0MPGGoJU2cICUlWgwuT2SjPH6CIIjMYDc5hgF9aXmrVCQSRBQAJkxcAmI4IPJqOgYmFjZObjsBIVEDNiNFFXVNbV1gzJk5U1jLBghrW3t05zc4+h8fWhwATxtoAFk20hRoAF5oAElEsHxgDAAVDowAHh6++ABhZilklAA+BvbOxejVxI4ufqHdjCQAMyiydAAfaHOrm5CG2kv2CZxpZmhL6RAeHgSwgc0O6xO8Bg2DwRBg+zI4KSkMG3V6BzWyJSmx4tjIEAkCJQAG0ALqKfxQQQAfgkSOOKQgZOMElGrHGkxmNjBmIZKAgmzJZji0muPAAhJSIIJTPAMDh2PB8JhojBICMxhNprMeUcNlDBaS3nE5QqlSr8QA6f6Amq46KmAabRjgKkQS0QJACHg8ADWrogjudeP6snZWue-oCxlM6rZOA52u59P1ArJDRaNASrEw6CG+AwAHcXmgeAA...

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

What are the benefits of adding member functions to the data structures of React.js store?

Using React.js and Typescript, I store plain Javascript objects in the React.js store. These objects are sometimes received from the server without any member functions, but I wish to add functions for better organization. Instead of having to rely on exte ...

Show blank value if there are no search results, along with an additional menu option

I am currently working on a Typeahead feature with a customized menu using the renderMenu method. In this setup, I have added 2 custom menu items at the bottom - one divider and a button. An issue arises when there are no search results. If I do not inclu ...

TypeScript seems to be failing to detect the necessary checks before they are used

I've been pondering on how to ensure TypeScript acknowledges that I am verifying the existence of my variables before using them. Below is the code snippet : Here's the function responsible for these checks: function verifyEnvVars(){ if (!proc ...

The error message states that the object literal can only define properties that are known, and in this case, 'tailwindcss' is not recognized in the type 'NuxtConfig'

I'm facing an issue in my nuxt.config.ts file while trying to set up a custom tailwind css path. The error I keep encountering can be viewed here. Can someone guide me on how to properly create the custom tailwind css path in my nuxt.config.ts file? ...

Consolidate multiple generic items into a single entry

In my current project, I am structuring the types for a complex javascript module. One of the requirements is to handle multiple types using generics, as shown in the snippet below: export interface ModelState< FetchListPayload, FetchListR ...

Steps for aligning the upper rectangular text in the center of the larger rectangular border

https://i.stack.imgur.com/7yr5V.png I was aware of a particular element in html that had text positioned in the upper left corner, but my knowledge didn't go beyond that. Should I be adjusting the translation on both the X and Y axes based on the par ...

Error: Attempting to change a read-only property "value"

I am attempting to update the input value, but I keep receiving this error message: TypeError: "setting getter-only property "value" I have created a function in Angular to try and modify the value: modifyValue(searchCenter, centerId){ searchCenter.va ...

Discovering the optimum route within a 2D array given specific limitations using Dynamic Programming

Hey, I have a query related to dynamic programming (dp) that goes like this: Input: A 2D array of numbers Output: The maximum sum of a path from (0,0) to (n-1,n-1) with the following conditions: You can only move down and right i.e. from (A[i-1][j]) t ...

Unable to retrieve device UUID using capacitor/device on Android

I'm currently attempting to obtain the UUID of my devices so that I can send targeted notifications via Firebase. My front end and back end are connected, enabling the back end to send notifications to the front end using Firebase. However, all I am a ...

Typescript's confidential variables

Currently, I am delving into the world of Angular2 and familiarizing myself with working with classes in javascript for the first time. I'm curious about the significance of using the private parameter in the constructor, as opposed to simply writing ...

Leveraging ngOnChanges to determine the display of an overlay based on input alterations

Working with TS/Angular on a web application, I made the decision to refactor some code that handles displaying different overlays. Instead of having separate code for each overlay, I consolidated them into one "master overlay" and created a function withi ...

Extract nested values within objects and arrays, and return the complete type of the original object

I have a dataset that resembles the structure of IconItems: { title: "Category title", description: "Example description", lists: [ { id: "popular", title: "Popular", items: [ { ...

Breaking down CSV rows and transforming numerical data (Typescript, Javascript)

Looking to convert a row from a csv file into an array and then transform the numeric values from string format. This represents my csv file row: const row = "TEXT,2020-06-04 06:16:34.479 UTC,179,0.629323"; My objective is to create this array (with the ...

What causes parameters to be undefined when making a DELETE request in my Next.js application running on version 14.1.4?

I am encountering an issue with my DELETE mapping export async function DELETE({params} : {params: {id: string}}) { try { const loanToDelete = await prisma.loan.findUnique({ where: { id: parseInt(params.id) } }) if (!loanToDelete ...

Angular2 Interactive Modal Pop Up

Here is an example of a modal in HTML code: <app-modal #modal1> <div class="app-modal-header"> header </div> <div class="app-modal-body"> You c ...

Displaying all notifications while using parameters in TypeScript

I am trying to display all of my notifications in HTML. The value is returned in res = response.json();, but my website only shows one notification, similar to the example in Let's start with this code: public eventsbyserial(id: string): Observab ...

How to refresh an array in Angular 4 after inserting a new element using splice method?

I have a Angular list displayed in a table, and I need to insert an element at a specific position. I have tried using the following code: array.splice(index, 0, element); While everything seems fine in the console with the element being added at the corr ...

Is there a way to modify the style within a TS-File?

I've created a service to define different colors and now I want to set separate backgrounds for my columns. However, using the <th> tag doesn't work because both columns immediately get the same color. Here's my code: color-variatio ...

How can I display every index from my JSON Fetched Files?

In the picture shown here, I have a series of Tables being displayed: https://i.sstatic.net/YUZD1.png The issue highlighted in red is that I want to show the Index of each JSON array as the Table number. Below is the code snippet: function getExternal( ...

Combining ReactJS event handling for onClick and onKeyDown into a single handler for TypeScript

To ensure accessibility compliance, I am incorporating onKeyPress handlers into my application. However, I am facing a challenge with interactive <div /> elements. Here are the event handlers I want to trigger on click: const handleViewInfoClick = ( ...