Guide on creating a generic type that depends on the arguments provided, specifically a union type

I am dealing with the following similar types:

class ActionFoo {
  action: 'foo';
  foo: string;
}
class ActionBar {
  action: 'bar';
  bar: number;
}

In addition, I have some handler functions for each type of defined "action", such as:

function fooHandler(action: ActionFoo) {
  // perform a task
}
function barHandler(action: ActionBar) {
  // perform a task
}

Now, the challenge is to create a utility function that can handle all these scenarios. Despite my attempts, the expected types for handlers were incorrect. Here's what I tried:

type ActionHandler<T extends { action: string }, O = any> = (action: T) => O; // defining the type for handlers

function handleAction<T extends { action: string }, O = any>(
  action: T,
  handlers: {[key in T['action']]: ActionHandler<T, O>}
) {
  const handler = handlers[action.action];
  if (handler) return handler(action);
  throw new Error('not implemented');
}

type Action = ActionFoo | ActionBar;

function run(action: Action) {
  return handleAction(action, {
    foo: action => fooHandler(action), // TypeScript compiler points out a type error (TS2345) due to missing 'foo' field
    bar: action => barHandler(action), // encountering a similar error
  });
}

Answer №1

At last, the solution has been found. In order to ensure the correct typing for union type Action in ActionHandler, it is necessary to merge the key type with the value type.

type ActionHandler<T extends { action: string }, O = any> = (action: T) => O; // defining the handler type

function handleAction<T extends { action: string }, O = any>(
  action: T,
  handlers: { [K in T['action']]: ActionHandler<T & { action: K }, O> } // <--- including & { action: K } to combine value type with key type
) {
  const handler = handlers[action.action];
  if (handler) return handler(action);
  throw new Error('not implemented');
}

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 is the correct way to start a typed Object in TypeScript/Angular?

As I delve into the world of Angular and TypeScript, I am faced with a dilemma regarding how to initialize an object before receiving data from an API request. Take for instance my model: //order.model.ts export class Order { constructor(public id: num ...

When using the delete method in Next.js, req.body is undefined

Strangely, I cannot figure out the reason why fetching data and inserting it into the body of my response results in an "undefined" message in the console. Interestingly, I have two nearly identical components - one employing a POST method with a populated ...

The "rest" variable is automatically assigned the type of "any" because it lacks a specified type and is used within its own initializer

Attempting to set up a private route using react router 4 and Typescript. Check out the code I'm working with: type CustomRouteProps<T> = T & { component: any, authRequired: boolean }; function PrivateRoute({ component: Component, authRequ ...

What is the process for clearing a selection from a table?

I have been facing this issue for some time now. I am working with a basic table where selecting a row highlights it. However, I want to enhance my button functionality by adding a "Remove Selection" feature. When clicked, I need the selected row to lose i ...

ReactJS: error occurs when trying to fetch data and encountering issues with reading properties

I am currently attempting to initiate an API call (a GET request) in order to download a document. However, I am encountering an error when making the API call: TypeError: Cannot read properties of undefined (reading 'payload') const printPin ...

Steps for enabling a function to return an undefined type

After extensive review, I have discovered that TypeScript has numerous hidden nuances, which make it less strict and accurate. I prefer to utilize 'undefined' as the return type for functions because it accurately reflects the reality of the sit ...

Angular developers are struggling to find a suitable alternative for the deprecated "enter" function in the drag and drop CDK with versions 10 and above

By mistake, I was working on an older version of Angular in StackBlitz (a code-pane platform). I came across a function called enter on GitHub, but it didn't solve my issue. I was working on a grid-based drag and drop feature that allows dragging bet ...

Employing Class Categories in Static Procedures

I am currently working on developing a foundational Model that will serve as the base for a specific model class, which will have an interface detailing its attributes. Within the base Model class, I am aiming to incorporate a static factory() function th ...

When the variable type is an interface, should generics be included in the implementation of the constructor?

Here is a code snippet for you to consider: //LINE 1 private result: Map<EventType<any>, number> = new HashMap<EventType<any>, number>(); //LINE 2 private result: Map<EventType<any>, number> = new HashMap(); When the ...

Step-by-step guide on activating a button only when all form fields are validated

My very first Angular 5 project. I've gone through resources like: https://angular.io/guide/form-validation and various search results I looked up, only to realize that they are all outdated. In my form, I have several input fields such as: <for ...

CreateAsyncModule using an import statement from a variable

When trying to load a component using defineAsyncComponent, the component name that should be rendered is retrieved from the backend. I have created a function specifically for loading the component. const defineAsyncComponentByName = (componentName: strin ...

Adjusting the value of a mat-option depending on a condition in *ngIf

When working with my mat-option, I have two different sets of values to choose from: tempTime: TempOptions[] = [ { value: 100, viewValue: '100 points' }, { value: 200, viewValue: '200 points' } ]; tempTimesHighNumber: TempOpt ...

Error: The variable _ is undefined when trying to use the .map() function on an array

While working on my project, I encountered a "ReferenceError: _ is not defined" when using the .map function in this code snippet: arr.map(async (elem) => { ... }); I couldn't find any explicit mention of "_" in my code. The error trace pointed me ...

Stop modal from closing in the presence of an error

My approach involves using a generic method where, upon adding a food item, a modal window with a form opens for the user to input their details. However, since backend validation for duplicate items can only be retrieved after the API call completes. I w ...

Error: JavaScript object array failing to import properly

In my code, I have an array of objects named trace which is defined as follows: export const trace: IStackTrace[] = [ { ordered_globals: ["c"], stdout: "", func_name: "<module>", stack_to_render: [], globals: { c: ["REF" ...

Is there a way to specify patternProperties in a JSON schema and then map it to a TypeScript interface?

I'm in the process of developing a TypeScript interface that corresponds to a JSON schema. The specific field in my JSON schema is as follows: "styles": { "title": "Style Definitions", &qu ...

The data from Angular2 Observable Subscription appears undefined, although a closer look at the Browser Debug reveals otherwise

Is it possible there is an issue with the data subscription process? Upon subscribing to data from a service call, 'undefined' is returned as the data set. Surprisingly, when I debug the code in the browser, it clearly shows that the correct dat ...

evaluate a utility function that operates on children

In managing a component that requires children (referred to as the layout component), there is a specific function nested within this component that processes these child components. Testing this function poses a challenge, so I decided to extract it into ...

Parent variable in Angular Type Script causing undefined error

Can anyone explain why I keep receiving an undefined error? export abstract class BaseEditorComponent implements IPropertyEditor, OnDestroy { @Input() public element: BpmnJS.IRegistryElement; --more code here export class CommonPropertiesEditorCo ...

Is it better to store data individually in localStorage or combine it into one big string?

When it comes to keeping track of multiple tallies in localStorage, one question arises: Is it more efficient to store and retrieve several small data points individually or as one larger chunk? For example: localStorage.setItem('id1', tally1); ...