Determining the argument type in Typescript based on the value of a previous argument

I am struggling to implement type checking on the payload argument of the notify method in the following class. Here is the code I have tried:

type UserNotificationTypes = {
  ASSIGNED_TO_USER: {
    assignedUserId: string
  }
  MAIL_MESSAGE_SENT: {
    receiverUserId: string
  }
}

export class UserNotificationService {
  notify: <TypeKey extends keyof UserNotificationTypes>(type: TypeKey, payload: UserNotificationTypes[TypeKey]) => void = (
    type,
    payload,
  ) => {
    if (type === 'ASSIGNED_TO_USER') {
      const a = payload.assignedUserId
    }

    if (type === 'MAIL_MESSAGE_SENT') {
      const b = payload.receiverUserId
    }
  }
}

When using TypeScript, an error is displayed:

Property 'assignedUserId' does not exist on type '{ assignedUserId: string; } | { receiverUserId: string; }'. Property 'assignedUserId' does not exist on type '{ receiverUserId: string; }'.

TS Playground link

Answer №1

Any potential solutions?

If we use mapped types to map each type to its payload, then we can combine them all into a union by referencing the mapped type like this:

type NotifyArgs = {
    [Type in keyof UserNotificationTypes]: [type: Type, payload: UserNotificationTypes[Type]];
}[keyof UserNotificationTypes];

This will yield:

[type: "EVENT_ASSIGNED_TO_USER", payload: {
    assignedUserId: string;
}] | [type: "MAIL_MESSAGE_SENT", payload: {
    receiverUserId: string;
}]

Now, you can destructure it in the function declaration as follows:

notify(...[type, payload]: NotifyArgs) {

Playground Link


Why is my code not functioning? (simplified)

The issue lies with generics. Although it seems logical that this should work, unfortunately, it does not. This becomes apparent when attempting to call it in this manner:

notify<"ASSIGNED_TO_USER" | "MAIL_MESSAGE_SENT">(...);

From TypeScript's perspective, this implies that payload could be either { assignedUserId: string } or { receiverUserId }, which poses difficulties when accessing properties on a union type.

Therefore, due to this potential issue, TypeScript prohibits such usage. Instead, we need to explicitly list out the possible arguments for the function:

notify(...[type, payload]: ["ASSIGNED_TO_USER", { assignedUserId: string }] | ["MAIL_MESSAGE_SENT", { receiverUserId: string }]) { 

This quickly becomes repetitive, and hence why we resort to using a mapped type to handle it efficiently.

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

Looking for a more efficient approach to writing React styles for color?

Desire I am interested in customizing colors within Material UI components. Moreover, I aim to develop a React Component that allows for dynamic color switching through Props. Challenge The current approach using withStyles results in redundant and lengt ...

What is the best way to access this nested array in order to display my <input> field?

I am currently working on displaying an input field for 'levels'. The ultimate goal is to have an add button that will allow users to create multiple levels. Each learningGoal should have its respective levels created before the learning goals ar ...

Experiment with Google Sign-In authentication in jest with Firebase

As I try to effectively mock firebase authentication with Google login, I am encountering some difficulties. Below is the code that I am currently working with: simple.tsx import React, { Component } from 'react'; import * as firebase from &apo ...

The argument supplied in Angular using TypeScript is incompatible: a 'string' type cannot be assigned to a parameter expecting an 'Element' type

I'm facing 3 errors with Typescript in angular when working with D3js elements. I tried to create a mouseover event to display tag and value data corresponding to the bar graph, but encountered issues. I attempted declaring strings and even added "noI ...

What is the best way to access a state variable that has a union data type?

Is there a way to access a field of a state variable with a union type in TypeScript? Here is an example of a zustand store I have defined: import { create } from 'zustand' type HeightUnit = 'cm' | 'ft\' in"&ap ...

Transforming AngularJS 2.0 code into ES6 syntax

Successfully implemented the AngularJS 2.0 5 Minute Quickstart in my IntelliJ IDEA 14.1.4 following a helpful Stackoverflow Answer on AngularJS 2.0 TypeScript Intellij idea (or webstorm) - ES6 import syntax. However, it seems to focus on compiling TypeScr ...

Is it possible to omit the expression of <T> when it is not necessary to define?

Is there a way to write code without using the <T> notation when it's not necessary? Here is what I have in mind: interface Props<?T> { className: string data?: T } const props: Props = {className: "hello, world"} const pro ...

Is there a more efficient method to tally specific elements in a sparse array?

Review the TypeScript code snippet below: const myArray: Array<string> = new Array(); myArray[5] = 'hello'; myArray[7] = 'world'; const len = myArray.length; let totalLen = 0; myArray.forEach( arr => totalLen++); console.log(& ...

Leveraging the Typescript Compiler API for transforming a typescript document

I am currently exploring the Typescript Compiler API to develop a tool that merges typescript files. I am curious if there is a way to: Modify the AST after parsing a .ts file. Convert the modified AST back into a .ts file. I have reviewed the documenta ...

Converting JSON objects into TypeScript classes: A step-by-step guide

My challenge lies in converting Django responses into Angular's User array. This conversion is necessary due to variations in variable names (first_name vs firstName) and implementing specific logic within the Angular User constructor. In simple term ...

Switch up row values in an array and transform them into an object using SheetJS

I am struggling to format an array where each "Working Day" is represented as an object with specific details like index and start/end date. I need help manipulating the JSON data to achieve the desired structure. The package I'm currently using is: ...

Can you specify a data type for ngFor in Angular?

I am currently employing ngFor to iterate over a collection of a specific type [Menu] within Angular 4.x. During this process, I am looping through a collection property of the menu object (menu.items). Unfortunately, my IDE (Eclipse + Angular IDE) is un ...

Mastering the concept of promise chaining through this straightforward example

I'm struggling to implement a logic where I need to compare the user's password to a given password and handle different scenarios based on the comparison result. Here's what I need to achieve: If the user doesn't exist, return undefi ...

Leveraging Class Types with Generics

Take a look at this example: https://www.typescriptlang.org/docs/handbook/2/generics.html#using-class-types-in-generics To make it work, I just need to call a static method before instantiation. Let's adjust the example like this: class BeeKeeper { ...

Utilizing vue-property-decorator: Customizing the attributes of @Emit

After seeing the @Emit feature, I checked out the example on GitHub. import { Vue, Component, Emit } from 'vue-property-decorator' @Component export default class YourComponent extends Vue { count = 0 @Emit() addToCount(n ...

Derive a subset Union from a Union in Typescript

Here is a scenario with a Union type I'm working with; type MyUnionType = 'foo' | 'bar' | 'baz' What I need to do is create a new Union called MySubUnion, which will be a subset of the original; type MySubUnion = &apos ...

Angular - Ensuring correct rendering of a subcomponent with input parameter on the first update

Here is a snippet of code showcasing a list of educations and a component: <cdk-virtual-scroll-viewport itemSize="5" class="list-scroll"> <app-education-item *ngFor="let education of loadedEducations" ...

What is preventing Apollo from achieving full transformation?

I have been struggling with an issue involving Apollo mutation for the past 2 days. Whenever I call a mutation on Angular Apollo generated code and subscribe to it, the subscription never completes. I am expecting a result from the server, but nothing is ...

Angular project facing issues during Maven build process

Hi there, I'm currently facing some challenges while trying to deploy my Angular and Spring Boot application. Whenever I run the command mvn clean compile spring-boot:run, I encounter a build failure from my Angular pom file. The error message I am r ...

Any ideas on how to fix the issue of 'type' property being absent in 'AsyncThunkAction' with Redux Toolkit while working with TypeScript?

While working with Redux Toolkit and the thunk/slice below, I decided to handle errors locally instead of setting them in state. To achieve this, I followed the example provided here. Although I could have set an error in the state, I wanted to explore wh ...