TS2339 error caused by update to Typescript v2.2.2 union type

Within my Angular 2 project, I am utilizing ngrx for managing state with actions and reducers. An example of the actions setup is as follows:

import { Action } from '@ngrx/store';

export const actionTypes = {
  ACTION_1: type('Actions 1'),
  ACTION_2: type('Actions 2'),
};

export class ActionOne implements Action {
  public type = actionTypes.ACTION_1;

  constructor(public payload: any) { }
}

export class ActionTwo implements Action {
  public type = actionTypes.ACTION_2;
}

export type Actions
  = ActionOne
  | ActionTwo;

In this setup, some actions come with a payload while others do not, and the Actions type acts as a union of ActionOne or ActionTwo. However, an error arises in my reducer -

Property 'payload' does not exist on type 'Actions' Property 'payload' does not exist on type 'ActionTwo'
.

The reducer function looks like this:

export function reducer(state = initialState, action: Actions): IState {
  switch (action.type) {

    case actions.actionTypes.ACTION_1: {
      return Object.assign({}, state, {
        data: action.payload,
      });
    }

    case ...
  }
}

This issue appeared after updating TypeScript version from 2.0.3 to 2.2.2. Any suggestions on how to resolve this error without having to include payload in every action? Perhaps there's a configuration option in tsconfig.json that can help in this situation?

Answer №1

To ensure proper functioning of discriminated union, it is recommended to declare constants within a namespace rather than a dictionary. This approach allows ACTION_1 and ACTION_2 to be assigned literal types.

export namespace actionTypes {
    export const ACTION_1 = 'Action 1';  // <-- type of ACTION_1 is 'Action 1' in TS 2.1+
    export const ACTION_2 = 'Action 2';
};

It's important for the type of each class to be a constant value, as this ensures the type is a literal type instead of a generic string.

export class ActionOne implements Action {
    public readonly type = actionTypes.ACTION_1;   // <-- note the `readonly`
    constructor(public payload: any) { }
}

export class ActionTwo implements Action {
    public readonly type = actionTypes.ACTION_2;
}

The use of ACTION_ONE: type('Action one') pattern has been deprecated by ngrx developers starting from TypeScript 2.1 / Angular 4. For more information, refer to https://github.com/ngrx/example-app/pull/88#issuecomment-272623083.

Answer №2

Within this switch statement, you are able to determine which action is being taken and therefore cast to the appropriate one:

case actions.actionTypes.ACTION_1: {
    return Object.assign({}, state, {
        data: (action as ActionOne).payload,
    });
}

It is clear that the compiler is correct in this scenario, as the union results in a type that only has shared properties, with payload not being one of them.

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 Figma plugin that was generated does not come with TypeScript typings included

As I attempt to follow the plugin setup guide located here, my plugin is quite simple yet effective. It consists of the following code: figma.showUI(__html__); // @ts-ignore console.log(figma.currentPage.selection[0].cornerRadius); When executed as is, t ...

What's the best way to address this blind spot?

Exploring the world of TypeScript has left me puzzled by a scenario where TypeScript does not perform type checking as expected. I'm running into an issue where 'this.a.method()' appears to be error-free when it should actually throw an erro ...

How to send variables to a function when a value changes in TypeScript and Angular

As someone who is new to Angular and HTML, I am seeking assistance with the following code snippet: <mat-form-field> <mat-select (valueChange)="changeStatus(list.name, card.name)"> <mat-option *ngFor="let i of lists"> {{i.name}} ...

Is it possible for an Object.prototype function in Typescript to return an instance of a Sub type?

I'm working on some code that involves defining classes like the following: class Place { next: Place; get to() : Place { return this; } } let places : Place[]= []; .. places[0].to.next = new Place(); Since there are many similar classes, ...

Build an Angular wrapper component for the phone textbox functionality

Looking to transform the Phone Mask solution below into an Angular component. Does anyone have a method to accomplish this? * Any solution that results in a similar component for a Phone textbox will suffice. Mask for an Input to allow phone numbers? ht ...

Enhancing UI-Grid: Implementing Dynamic Field Addition in the Header Name Section

https://i.sstatic.net/0jyFI.png There is a grid with a field named Users, and the requirement is to display the count of Users in the header name of a ui-grid. How can I achieve this? This snippet shows my JavaScript file code: var userCount = response.u ...

"Struggling to Get Angular2 HttpClient to Properly Map to Interface

Currently, I am integrating Angular with an ASP.NET WebApi. My goal is to transmit an object from the API to Angular and associate it with an interface that I have defined in Typescript. Let me show you my TypeScript interface: export interface IUser { ...

How to start Angular2 prototype with an object literal

export abstract class GridColumn { public field?: string; public sortField?: string; public header?: string; public footer?: string; public sortable?: any = true; public editable?: boolean = false; public filter?: boolean = true ...

Please ensure that the subscription data is fully loaded before utilizing it as input

Recently, I have been developing a service that retrieves a list of users to be used as input for a child component. However, I encountered an issue where the component loads before the users list is fully loaded. One solution I came up with is to implemen ...

Fixing the (Missing: any) error in a create-react-app project using TypeScript

One of the challenges I'm facing is when I call the BookTracker component in my root App.tsx file, specifically with the prop book={MY_MOCK}. type BookParamsTypes = { title: string; pubDate: number; //... rest }; import { BookParamsTypes } fro ...

distinct section dedicated to managing angular materials

In order to avoid importing modules directly from angular materials, I decided to create a separate angular module called AppCustomMaterialModule and imported it into the main module AppModule. The issue arises when trying to use one of the exported angula ...

Observing the World with TypeScript

Sorry, I am quite new to this and facing a bit of confusion. So, I have a CalendarService which includes a method called getYear(id: string). The structure of my Year model is as follows: export class Year { id: string; number: Number; months: ...

What is the best method to merge two arrays into a single array of objects?

Is it possible to utilize an ngFor directive instead of duplicating the <table> element twice? (Note: I considered consolidating all items into objects within a single array for mapping purposes (each object containing a variable, label, and value) ...

Encountered an error when creating my own AngularJS module: Unable to instantiate

Attempting to dive into TypeScript and AngularJS, I encountered a perplexing error after following a tutorial for just a few lines. It appears that there may be an issue with my mydModule? angular.js:68 Uncaught Error: [$injector:modulerr] Failed to inst ...

Wait until a svelte store value is set to true before fetching data (TypeScript)

I have implemented a pop-up prompt that requests the user's year group. Since I have databases for each year group, I need to trigger a function once the value of userInfo changes to true. My JavaScript skills are limited, and my experience has been ...

Utilize Type Script/Angular 8 for seamless file management by either dragging and dropping files or selecting

Can you provide guidance on using the ng-upload-file plugin with TypeScript? https://github.com/danialfarid/ng-file-upload I attempted to implement it but encountered difficulties. Do you have a working sample available or know of another open-source plu ...

Using ngModel to bind data within an Angular dialog box

I'm facing an issue with my project where changes made in the edit dialog are immediately reflected in the UI, even before saving. This causes a problem as any changes made and then canceled are still saved. I want the changes to take effect only afte ...

In Angular 6, when using Put and Post methods with HttpParams, the return type can be modified to Observable<HttpEvent<T>>

I encountered a situation where I am performing a POST request and receiving a JSON object in response. When I use only the URL and body parameters in the POST request, the return type is Observable<T>. However, when I include additional parameters ...

Why is Sentry choosing to overlook certain errors in my React application?

While using Sentry to catch errors in my React app, I discovered that it ignores errors with 502 and 504 HTTP codes as well as some React errors. I am unsure why this is happening and would like to modify this behavior. Below is the initialization functio ...

The event triggered by the tinymce editor does not immediately refresh the Angular Component

I am currently working on creating an Angular application using a WordPress instance of TinyMCE. Within the editor, there are non-content-editable elements that trigger a modal window to open when clicked. However, I have encountered an issue where the mo ...