Encountering a Typescript issue stating "Property 'then' does not exist" while attempting to chain promises using promise-middleware and thunk

Currently, I am utilizing redux-promise-middleware alongside redux-thunk to effectively chain my promises:

import { Dispatch } from 'redux';

class Actions {
    private static _dispatcher: Dispatch<any>;
    public static get dispatcher(): Dispatch<any> {
        return Actions._dispatcher;
    }
    public static test() {
        this.dispatcher({
            type: 'MY_ACTION',
            payload: new Promise(resolve => resolve('hi'));
        }).then(result => {
            console.log(result); // It's working fine
        });
    }
}

The code snippet above is functional but triggers a warning message during compilation:

TS2339: Property 'then' does not exist on type '{ type: string; payload: Promise<{}>; }'

It appears that I must include Promise<...> somewhere as a type so TypeScript recognizes that then is indeed a property of the object returned by dispatcher(). Despite my efforts, I have yet to resolve this error.

https://github.com/gaearon/redux-thunk/issues/103

import { Dispatch } from 'redux';
import { ThunkAction } from 'redux-thunk';
import { getStore, IState } from './my_store';

let store = getStore();

// Define myThunkAction function with type ThunkAction<R, S, E>
let myThunkAction: ThunkAction<Promise<string>, IState, null> =
    (dispatch: Dispatch<IState>, getState: () => IState) => {
        return new Promise<string>((resolve, reject) => {

            // Perform asynchronous operations using getState() and dispatch(), then...
            resolve('done!');

        });
    }

store.dispatch(myThunkAction)
.then(() => {
    // Execute actions after the thunk has completed...
});

This seems relevant, but where should I specify the action type, for example, MY_ACTION?

Answer №1

Upon examining this TypeScript playground example, it is evident that the variable a shares the same keys as the type of Dispatch<any>. If you hover over the error, you'll notice that the error message aligns with your situation. To access the promise and utilize the then function, you need to retrieve the payload from the Dispatch object.

this.dispatcher({ ... }).payload.then(....);

Edit1:

A quick review of the redux typings on GitHub reveals the Dispatcher interface.

export interface Dispatch<S> {
    <A extends Action>(action: A): A;
}
export interface Action {
  type: any;
} 

By rearranging and using pseudocode liberally, we can infer that the Dispatch type is a function that takes an object argument and returns an object of the same type.

type Dispatch: (action: {type: any, ...}) => {type: any, ...}

Both the input and output objects adhere to this structure:

interface {
    type: any,
    [key: string]: value
}

In conclusion, the issue could stem from either 1) not using official redux typings, 2) errors in the official typings, or 3) overlooked details in the live environment where the code fails to work.

Edit2:

I have not tested this code, so I cannot guarantee its effectiveness in resolving your problem. One approach could involve redefining the Dispatch interface.

declare module 'redux' {
    export interface Action {
       type: any;
    }
    export interface Dispatch<S> {
        <A extends Action>(action: A): Promise<S>;
    }
}

This TypeScript snippet is valid, as demonstrated in this playground. However, since I haven't personally encountered this scenario before, there may be potential issues implementing it.

If the above method does not work, another option is defining a namespace with the identical name as the module.

namespace redux {
    export interface Action {
       type: any;
    }
    export interface Dispatch<S> {
        <A extends Action>(action: A): Promise<S>;
    }
}

As I have not experimented with this approach previously, I cannot assure its success.

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

In Vue3, have you ever wondered why the $emit function seems to work fine before a promise fetch,

I have encountered an issue while attempting to pass the result of a promise fetch from a child component to a parent component using emit. Strangely, the emit function was working perfectly fine before the $fetch operation, allowing my parent component to ...

The power of Vue reactivity in action with Typescript classes

Currently, I am working on a Vue application that is using Vue 2.6.10 along with Typescript 3.6.3. In my project, I have defined a Typescript class which contains some standard functions for the application. There is also a plugin in place that assigns an ...

Avoiding type errors in d3 v5 axis by using Typescript

I am new to TypeScript and I have some code that is functioning perfectly. I believe if I define a type somewhere, d3's generics will come into play? Within my code, I have an xAxis and a yAxis. Both are the same, but D3 seems to have an issue with t ...

Connecting two tables in an express API

Currently, I am in the process of developing an API using Express.js. At this stage, my initial tests are functioning correctly. My goal now is to retrieve values from two separate tables. For example, consider the following 2 tables: Table_A Id: 1, Name: ...

Reacting to each change event, Angular dynamically loads new autocomplete options

I am facing an issue with my form where users need to select a company using mat-select-search. Upon selection, an API call is made with the selected company ID to fetch users from that company for the autocomplete feature in recipient fields. The process ...

Executing a designated assessment in Protractor

Is there a way to run a specific test scenario in my Angular app? I recently added a new feature in Protractor, created the necessary page and steps, but I already have other features implemented. I am wondering if it is possible to solely test the new f ...

Sails encountering CORS preflight error due to cross-origin request

I am new to creating hybrid apps and have been following some tutorials. However, I encountered these errors on my browser console: Refused to load the script 'http://192.168.1.142:35729/livereload.js?snipver=1' due to Content Security Policy di ...

Look for and choose various fields from a lengthy JSON object

I am working with a JSON object that contains a large list of offerValue objects. { "Code": 0, "response": "SUCCESS", "offerValue": [ { "id": "111", "name": "ABC", "flag": "V" }, { ...

Angular 8's array verification feature lacks the ability to recognize preexisting elements

I've been trying to add and delete items in an array when a user selects or deselects the same item. However, it appears that either my array is not working properly or there is a bug in my code causing it to fail. <div class="grp-input"> ...

Restrictions on file sizes when using multer for file uploads

I am currently working on a file uploader that needs to support various file types, such as images and videos. My goal is to apply different maximum file sizes for images (10MB) and videos (100MB) using a single instance of Multer, a middleware designed fo ...

Is a 'Virtual DOM' included in React Native's architecture?

According to the ReactJS wiki page on Virtual DOM: React uses an in-memory cache of data structures to efficiently compute differences and update the displayed DOM in the browser. This allows developers to write code as if the entire page is re-rendered ...

Troubleshooting a Node.js server issue with a two-dimensional array

I am currently facing an issue with submitting a form that contains two-dimensional array fields on a post request in node.js. The problem lies in the fact that the server side is receiving a one-dimensional array with all the values combined. Below is an ...

Establish a connection to an already existing database using Mongoose

I've been exploring the inner workings of MongoDB lately. After setting up a local database, I created a collection with the following document: db.user.find().pretty() { "_id" : ObjectId("5a05844833a9b3552ce5cfec"), " ...

Suggestions to reduce our website loading time

Query: How can one effectively reduce the file size of a webpage to improve loading speed? What specific optimization practices and coding techniques (in JavaScript and PHP) can be implemented to decrease page weight? Motivation: After reading an article ...

Tips for customizing the AjaxComplete function for individual ajax calls

I need help figuring out how to display various loading symbols depending on the ajax call on my website. Currently, I only have a default loading symbol that appears in a fixed window at the center of the screen. The issue arises because I have multiple ...

Having trouble with a JavaScript function as a novice coder

Hello, I'm still getting the hang of JavaScript - just a few days into learning it. I can't figure out why this function I'm calling isn't functioning as expected. Here's the content of my HTML page: <!doctype html> <htm ...

How to prevent unnecessary new instances from being created by the Inject() function in Angular

Can someone please clarify if the inject() function provides different instances of a service? I suspect this might be why my code is not functioning as expected. Let's examine the code snippet below: { path: 'recipes', comp ...

After successfully creating an account, the displayName consistently appears as null

I have a Vue project that utilizes Firebase as the backend. User registration is done using email and password. Below is the method used in Firebase: firebase.auth() .createUserWithEmailAndPassword(this.user.email, this.user.password) . ...

Ways to standardize the input email address?

While using express-validator, I came across an issue where I used normalize email for validation of email during sign up and stored the normalized email on the server. Validation code: router.post( "/signup", [ check("name").n ...

Internationalization in Angular (i18n) and the powerful *ngFor directive

Within my Angular application, I have a basic component that takes a list of strings and generates a radio group based on these strings: @Component({ selector: 'radio-group', templateUrl: `<div *ngFor="let item of items"> ...