redux-thunk
redux-thunk copied to clipboard
ThunkAction is not assignable to parameter of type 'AnyAction'.
Bug Report
Package name / version
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.
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
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.
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 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.
@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
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';
Got the same issue, only for react-redux v8. Would love to have a fix for this.
@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?
I'll repeat my usual refrain :)
REPROS! WE NEED REPROS!
We really need actual projects that show the error happening so I can investigate.
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
- Use
Huh, good eye! I suppose I accidentally "fixed" this while doing some cleanup on the types :)
@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.
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 v8useDispatch
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 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.
@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 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.
@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.
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.
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.
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.
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.
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.
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",
@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
}
}
Today I ran into the same problem when I wanted to dispatch
initiate action for my API endpoint from redux-toolkit/rtk-query
library.
@reduxjs/toolkit: 1.8.2 react-redux: 8.0.2
How are you dispatching it? If you're using useDispatch
make sure you're using a typed hook.
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:
@sevgeek : can you show your actual store setup code, preferably as a CodeSandbox or Github repo?
@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
.
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
@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 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! 👍🏻