Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript error "Property 'then' does not exist" when chaining promises with promise-middleware + thunk

I'm using redux-promise-middleware with redux-thunk in order to 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); // this works
        });
    }
}

The code above works but also generates a warning during compile time:

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

It sounds like I need to include Promise<...> somewhere as a type so typescript knows that then is in fact a property on the object that's returned by dispatcher() but I haven't been able to remove the 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();

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

            // do async stuff with getState() and dispatch(), then...
            resolve('done!');

        });
    }

store.dispatch(myThunkAction)
.then(() => {
    // do stuff after the thunk has finished...
});

Seems related but where I can specify the action type i.e. MY_ACTION?

like image 229
FuzzyTree Avatar asked Nov 01 '17 20:11

FuzzyTree


1 Answers

As you can see in this ts playground the variable a exposes the same keys as the type ofDispatch<any>, and as you can see if you mouse over the error, the error message is the same as in your case. In order to access the promise (and there for the then function) you will have to access the payload of the Dispatch object.

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

Edit1:

If we take a look at the typings for redux we can quite quickly find the Dispatcher interface.

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

And then through some rewriting and some liberal use of psudocode, we can deduct that the type of Dispatch is a function that takes one argument witch is an object and returns an object of the same type as the argument.

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

Both the input object and the output object is of the type:

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

In conclusion, either 1) you are not using the official typings for redux, 2) the official typings for redux is wrong, or 3) you have missed something in your live environment were in fact the code doesn't work.

Edit2:

I haven't tried this code, so i have no idea if it will actually solve your problem. But you could try to redefine the Dispatch interface.

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

It is valid typescript as you can see in this playground, but I haven't had to do this my self before, so this might not work out of the box.

If that doesn't work you could try defining a namespace with the same name as the module.

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

Still I haven't tried this before, so I cant guarantee it will work.

like image 189
Olian04 Avatar answered Nov 13 '22 16:11

Olian04