redux-thunk icon indicating copy to clipboard operation
redux-thunk copied to clipboard

ThunkAction is not assignable to parameter of type 'AnyAction'.

Open mendesbarreto opened this issue 2 years ago • 31 comments

Bug Report

Package name / version

[email protected]

Description

I am trying to use a simple method from my store:

store.dispatch(ThunkAction);

It is working on run time, but I am receiving an error message on Typescript:

TS2345: Argument of type 'ThunkAction<Promise<AuthorizationDto | null>, State, unknown, SetAuthorizationAction>' is not assignable to parameter of type 'AnyAction'.   Property 'type' is missing in type 'ThunkAction<Promise<AuthorizationDto | null>, State, unknown, SetAuthorizationAction>' but required in type 'AnyAction'. 

I already tried to import the extend-redux like that:

import 'redux-thunk/extend-redux';

But the problem is the run time does not find file =S.

Screen Shot 2022-04-13 at 3 36 16 PM

I already tried every single solution that's I found on the previous issues, but non of them worked for me.

Steps to reproduce

  • Install the redux-thunk using "npm install"
  • Create a ThunkAction
  • Call the store.dispatch(ThunkAction)
  • See typescript Type error

Expected behavior

No error message from typescript when using store.dispatch(ThunkAction)

Environment

  • OS: macOS Monterey 12.3.1
  • Node/npm version: node v16.14.2 / npm 8.5
  • Platform: React Native iOS/Android

mendesbarreto avatar Apr 13 '22 19:04 mendesbarreto

Do you actually have the store configured to use the thunk middleware?

It would really help to see a CodeSandbox or a repo that shows this problem happening.

markerikson avatar Apr 13 '22 21:04 markerikson

I will create a project. But is really simple to reproduce, you just install the [email protected] in a typescript project and try to use store.dispatch(ThunkAction); from a redux store.

mendesbarreto avatar Apr 20 '22 00:04 mendesbarreto

@mendesbarreto unfortunately I don't know what the ThunkAction variable looks like in this example, or how the middleware was set up, or what TS version you're using, or how TS itself is configured. So, I really need to see an actual project if at all possible, so I can investigate it myself.

markerikson avatar Apr 20 '22 02:04 markerikson

@mendesbarreto Are you by any chance using react-redux v8? I have created the following Issue.

https://github.com/reduxjs/react-redux/issues/1911

maybe related https://github.com/reduxjs/redux-thunk/pull/247

yamachu avatar Apr 20 '22 03:04 yamachu

For me import 'redux-thunk/extend-redux'; was causing a Webpack error:

ERROR in ./source/scripts/app.tsx 5:0-34
Module not found: Error: Can't resolve 'redux-thunk/extend-redux' in 'C:\Users\nbier\Documents\test-app'

I ended up using an empty type-import to clue Webpack into the fact I wasn't trying to import a JS module:

import type {} from 'redux-thunk/extend-redux';

Methuselah96 avatar Apr 23 '22 16:04 Methuselah96

Got the same issue, only for react-redux v8. Would love to have a fix for this.

allicanseenow avatar Apr 26 '22 00:04 allicanseenow

@allicanseenow

react-redux v8 was where I was running into the issue as well. Are you importing redux-thunk/extend-redux at all? Did you try import type mentioned in my previous comment? What's the exact error message you're getting?

Methuselah96 avatar Apr 26 '22 01:04 Methuselah96

I'll repeat my usual refrain :)

REPROS! WE NEED REPROS!

We really need actual projects that show the error happening so I can investigate.

markerikson avatar Apr 26 '22 01:04 markerikson

FWIW, I believe the reason this is coming up as an issue with react-redux v8 is because the v7 useDispatch() was typed as:

// NOTE: the first overload below and note above can be removed if redux-thunk typings add an overload for
// the Dispatch function (see also this PR: https://github.com/reduxjs/redux-thunk/pull/247)
export function useDispatch<TDispatch = Dispatch<any>>(): TDispatch;
export function useDispatch<A extends Action = AnyAction>(): Dispatch<A>;

The first function overload means you can dispatch literally anything (as a workaround for redux-thunk).

But in v8 it's now typed as:

export declare const useDispatch: <AppDispatch extends Dispatch<AnyAction> = Dispatch<AnyAction>>() => AppDispatch;

which only allows for Action objects.

So this issue isn't "new" it's just been masked by the loose types in the old react-redux type definitions.


That's just background for why this is coming up now. If you're hitting this error now as a user you should either:

  • Use Redux Toolkit and define typed hooks (which will be correctly typed and allow thunk actions)
  • If you're using vanilla Redux, most bundlers won't like import 'redux-thunk/extend-redux' in a regular TypeScript file because they think you're trying to import JavaScript so you can either:
    • Use import type {} from 'redux-thunk/extend-redux'
    • Put /// <reference types="redux-thunk/extend-redux" /> in a .d.ts file that's included in your type-checking
    • Put import 'redux-thunk/extend-redux' in a .d.ts file that's included in your type-checking

Methuselah96 avatar Apr 26 '22 04:04 Methuselah96

Huh, good eye! I suppose I accidentally "fixed" this while doing some cleanup on the types :)

markerikson avatar Apr 26 '22 04:04 markerikson

@markerikson Here's your repro. :)

The line I linked to errors with Webpack because it's trying to import 'redux-thunk/extend-redux' as a JS module.

Methuselah96 avatar Apr 26 '22 04:04 Methuselah96

Heh, thanks :)

So backing up, it sounds like there's two different "issues" going on here:

  • ThunkAction not dispatching correctly, which we think is due to a change in the v8 useDispatch types when the user wasn't actually following our TS setup guidelines
  • The separate question of "what is the right way to import the extended global types". Given that it's a pure types-only file, I think it's safe to assume that it has to be done as import type and I just flat-out was wrong with my import instructions.

Is there anything else going on atm beyond those? Seems like we could consider this resolved if not.

markerikson avatar Apr 26 '22 04:04 markerikson

@markerikson Thanks for looking into this. I am at work but I'll see if I can reproduce the bug locally later. The bug occurred to me in a project where I don't use the toolkit but the old redux action/reducer pattern instead.

allicanseenow avatar Apr 26 '22 04:04 allicanseenow

@Methuselah96 I was just importing directly import { ThunkAction } from 'redux-thunk'. I'll test out the other way to import later as I have recently reverted the react-redux version back to v7.

allicanseenow avatar Apr 26 '22 04:04 allicanseenow

@allicanseenow Yeah, an import from 'redux-thunk' won't extend the Redux types to allow dispatching thunk actions, you have to import 'redux-thunk/extend-redux' somewhere.

Methuselah96 avatar Apr 26 '22 13:04 Methuselah96

@markerikson Yeah, that sounds right.

Although on your first point, for vanilla Redux users the TypeScript guide does not mention the need to import redux-thunk/extend-redux. Without that import the most obvious way of typing a useDispatch hook with Dispatch<ThunkAction> will not work. But Redux Toolkit should work fine.

Methuselah96 avatar Apr 26 '22 14:04 Methuselah96

In theory, our advice of type AppDispatch = typeof store.dispatch ought to work even with vanilla createStore + applyMiddleware, but it's been so long since I've tried it that I'm not 100% sure.

markerikson avatar Apr 26 '22 14:04 markerikson

Yeah if the middleware is the only enhancer it might work, but I don't think composing multiple enhancers with the Redux types works right now without https://github.com/reduxjs/redux/pull/3776 (can we get that merged :)). I haven't tested it out either, so maybe it would work.

Methuselah96 avatar Apr 26 '22 14:04 Methuselah96

Can confirm that type AppDispatch = typeof store.dispatch does not work with vanilla createStore + applyMiddlware. Here's the CodeSandbox (not a working app, just checking the types). I didn't get an error in the TypeScript playground, but that might be because we haven't removed @types/react-redux. I don't think the PR mentioned above would fix this scenario, but I haven't dug into why this doesn't work.

Methuselah96 avatar Apr 26 '22 16:04 Methuselah96

Huh, interesting. We do have a bunch of custom type-level code in configureStore to ensure that dispatch ends up with the correct types based on the supplied middleware. I would have thought that applyMiddleware would at least do some of that behavior.

markerikson avatar Apr 26 '22 17:04 markerikson

It should, but I don't think people rely on createStore doing that in practice since it's been at least partially broken for a long time.

Methuselah96 avatar Apr 26 '22 18:04 Methuselah96

I'm using Redux Toolkit and upgrading the package version to at least 1.7.0 solved this issue for me.

"@reduxjs/toolkit": "1.7.2",

blumk avatar Jun 01 '22 08:06 blumk

@Methuselah96's solution pointed me in the right direction but since redux-thunk/src/types.ts currently has a typescript error which caused my pipelines to fail, I created a redux-thunk.d.ts file:


// This is required to fix redux thunk errors introduced with react-redux version 8
import 'redux'

declare module 'redux' {
    /**
     * Overload for bindActionCreators redux function, returns expects responses
     * from thunk actions
     */
    function bindActionCreators<
        ActionCreators extends ActionCreatorsMapObject<any>
    >(
        actionCreators: ActionCreators,
        dispatch: Dispatch
    ): {
        [ActionCreatorName in keyof ActionCreators]: ReturnType<
            ActionCreators[ActionCreatorName]
        > extends ThunkAction<any, any, any, any>
            ? (
                  ...args: Parameters<ActionCreators[ActionCreatorName]>
              ) => ReturnType<ReturnType<ActionCreators[ActionCreatorName]>>
            : ActionCreators[ActionCreatorName]
    }

    /*
     * Overload to add thunk support to Redux's dispatch() function.
     * Useful for react-redux or any other library which could use this type.
     */
    export interface Dispatch<A extends Action = AnyAction> {
        <ReturnType = any, State = any, ExtraThunkArg = any>(
            thunkAction: ThunkAction<ReturnType, State, ExtraThunkArg, A>
        ): ReturnType
    }
}


flocbit avatar Jun 15 '22 10:06 flocbit

Today I ran into the same problem when I wanted to dispatch initiate action for my API endpoint from redux-toolkit/rtk-query library.

image

@reduxjs/toolkit: 1.8.2 react-redux: 8.0.2

sevgeek avatar Jun 20 '22 11:06 sevgeek

How are you dispatching it? If you're using useDispatch make sure you're using a typed hook.

Methuselah96 avatar Jun 20 '22 12:06 Methuselah96

How are you dispatching it? If you're using useDispatch make sure you're using a typed hook.

@Methuselah96 Yes, I am using typed useAppDispatch hook in custom hook:

export type { RootStore, AppDispatch };

export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootStore> = useSelector;

export default store;
function useDispatchSafeDealAction() {
  const dispatch = useAppDispatch();

  const dispatchAction = React.useCallback(() => {
    dispatch(OAuthAPI.endpoints.getOAuthToken.initiate());
  }, [dispatch]);

  return [dispatchSafeDealAction];
}

Typescript error:

image

sevgeek avatar Jun 20 '22 13:06 sevgeek

@sevgeek : can you show your actual store setup code, preferably as a CodeSandbox or Github repo?

markerikson avatar Jun 20 '22 19:06 markerikson

@sevgeek : can you show your actual store setup code, preferably as a CodeSandbox or Github repo?

@markerikson Yes, sure. I use for configure store very simple reducer, my API slice reducer and additional listener middleware. https://codesandbox.io/s/react-redux-toolkit-wbzkgc?file=/src/store/index.ts

I think I found a script to reproduce the error.

When I disable the connection of my [APISlice.reducerPath], type AppDispatch gets the correct types and allows me to work with ThunkDispatch.

image

When I return connection my [APISlice.reducerPath], AppDispatch get a different type and then I get type error in my custom hook: https://codesandbox.io/s/react-redux-toolkit-wbzkgc?file=/src/hooks/index.tsx

image image

sevgeek avatar Jun 21 '22 06:06 sevgeek

@sevgeek : I think the issue here is the use of .prepend([SomeMiddleware]), and more specifically, the use of an array as the argument.

If I uncomment that API slice reducer, and then remove the square brackets from both the .prepend and .concat lines, the AppDispatch type looks to be correct.

We do some complex types manipulation to figure out how middleware might alter the type of store.dispatch, and it looks like that just doesn't work right if you pass in an array as an argument. (In the case where you wanted to add multiple middleware at once, you pass them in as separate args, like .concat(middleware1, middleware2).)

markerikson avatar Jun 21 '22 14:06 markerikson

@markerikson I transferred the pluggable middlewares from .prepend([]) to .concat(middleware1, middleware2, ...) or .concat([middleware1, middleware2, ...]) and the type error disappeared. Thank you very much for your help! 👍🏻

sevgeek avatar Jun 21 '22 19:06 sevgeek