@types/react-redux package and had problems.Definitions by: in index.d.ts) so they can respond.Hi @surgeboris, I see that your recent changes intend is to "properly support thunks". However they seem to have had the exact opposite effect in our code base!
This code works in 6.0.9:
export const OrderForm = connect(
(state: State) => ({
order: getOrder(state),
initialOrder: Order.createEmpty(),
isConfirming: isConfirming(state),
externalQuote: getExternalQuote(state),
panelQuote: getPanelQuote(state),
}),
{
onCancel: cancelOrder,
fetchExternalQuote,
fetchPanelQuote,
fetchQuoteQueued,
confirmOrder,
createOrder,
}
The new error in 6.0.10:
Argument of type 'typeof OrderForm' is not assignable to parameter of type 'ComponentType<Matching<{ order: Readonly<PermittedOrderParams>; initialOrder: PermittedOrderParams; isConfirming: boolean; externalQuote: Readonly<ThirdPartyQuote>; panelQuote: Readonly<PanelQuote>; } & { ...; }, Props>>'.
Type 'typeof OrderForm' is not assignable to type 'ComponentClass<Matching<{ order: Readonly<PermittedOrderParams>; initialOrder: PermittedOrderParams; isConfirming: boolean; externalQuote: Readonly<ThirdPartyQuote>; panelQuote: Readonly<PanelQuote>; } & { ...; }, Props>, any>'.
Types of parameters 'props' and 'props' are incompatible.
Type 'Matching<{ order: Readonly<PermittedOrderParams>; initialOrder: PermittedOrderParams; isConfirming: boolean; externalQuote: Readonly<ThirdPartyQuote>; panelQuote: Readonly<PanelQuote>; } & { ...; }, Props>' is not assignable to type 'Readonly<Props>'.
Types of property 'confirmOrder' are incompatible.
Type 'ThunkAction<void, State, void, Action<any>>' is not assignable to type '(usabilityTestId: number, priceInCents: number) => void'.
This was all working perfectly before, in fact 6.0.9 (which I'll be sticking to for now) was fantastic in that I was able to remove many of my type hacks. With your new version I'm forced to go back to my old tricks:
const mapDispatchToProps = {
onCancel: cancelOrder,
fetchExternalQuote,
fetchPanelQuote,
fetchQuoteQueued,
confirmOrder,
createOrder,
}
export const OrderForm = connect(
(state: State) => ({
order: getOrder(state),
initialOrder: Order.createEmpty(),
isConfirming: isConfirming(state),
externalQuote: getExternalQuote(state),
panelQuote: getPanelQuote(state),
}),
{
onCancel: cancelOrder,
fetchExternalQuote,
fetchPanelQuote,
fetchQuoteQueued,
confirmOrder,
createOrder,
}
// :'(
mapDispatchToProps as Pick<ImplProps, keyof typeof mapDispatchToProps>
)(Impl)
Alternatively you could provide some guidance on how these types are intended to be used?
Alternatively you could provide some guidance on how these types are intended to be used?
You can take a look at the test I wrote.
In a nutshell the main condition is to have return type of action creator's prop in WrappedComponent to match the return type of actual action creator.
@rhys-vdw I suspect that in your codebase the problem is in ImplProps. Can you post it here along with the ConfirmOrder actual type signature?
I've just hit this in a very new project. This action creator:
export const clickTile = (x: number, y: number) => {
return (dispatch: Dispatch, getState: () => State) => {
const state = getState();
if (selectTile(state, { x, y }).status !== "dug") {
return dispatch(updateTile(x, y));
}
};
};
is added to this connect:
export const Tile = connect(
(state: State, props: Pick<Props, "x" | "y">) => {
return selectTile(state, { x: props.x, y: props.y });
},
{ clickTile, startSelection },
)(TileBase);
When moused over:

The typings are cutting out the outer function, not the thunk.
@threehams Correct, that's the usecase I haven't thought about.
I'll address it with the follow up PR. I'm going to expand type definition to check if the function returned by ThunkActionCreator also returns a function, and if it is - I'll cut the inner one.
@surgeboris
I suspect that in your codebase the problem is in ImplProps. Can you post it here along with the ConfirmOrder actual type signature?
Props here is aliased as ImplProps
interface Props {
availableCountries: ReadonlyArray<string>
isConfirming: boolean
panelQuote: Readonly<PanelQuote>
externalQuote: Readonly<ThirdPartyQuote>
usabilityTestId: number
confirmOrder: (usabilityTestId: number, priceInCents: number) => void
createOrder: (
usabilityTestId: number,
targetPanel: Panel,
order: Readonly<PermittedOrderParams>
) => void
order: Readonly<PermittedOrderParams>
fetchQuoteQueued: () => void
fetchPanelQuote: (
usabilityTestId: number,
order: Readonly<PermittedOrderParams>
) => void
fetchExternalQuote: (
usabilityTestId: number,
order: Readonly<PermittedOrderParams>
) => void
initialOrder: Readonly<PermittedOrderParams>
onCancel: () => void
}
And these are the action creator thunks:
export const fetchExternalQuote = (
usabilityTestId: number,
order: Readonly<PermittedOrderParams>
): AsyncThunkAction<State> => async dispatch => {
// ...
}
export const fetchPanelQuote = (
usabilityTestId: number,
order: Readonly<PermittedOrderParams>
): AsyncThunkAction<State> => async dispatch => {
// ...
}
export const confirmOrder = (
usabilityTestId: number,
princeInCents: number
): ThunkAction<State> => dispatch => {
// ...
}
export const createOrder = (
usabilityTestId: number,
targetPanel: Panel,
order: Readonly<PermittedOrderParams>
): AsyncThunkAction<State> => async dispatch => {
// ...
}
And for completeness here is my AsyncThunkAction helper:
import { ThunkAction as ReduxThunkAction } from "redux-thunk"
import { Action } from "redux"
export type ThunkAction<State, Return = void> = ReduxThunkAction<
Return,
State,
void,
Action<any>
>
export type AsyncThunkAction<State, Return = void> = ThunkAction<
State,
Promise<Return>
>
@surgeboris
You can take a look at the test I wrote.
Your test is has a specific Promise return type. I wonder if this is why it passes? I haven't had a chance to look into it.
Your test is has a specific Promise return type. I wonder if this is why it passes?
@rhys-vdw I did not understood the issue fully. Now that you've posted complete information - your issue is about parametrized thunks, which is something I haven't thought about.
I'll try to address it in the follow up PR once I'll have some spare time for that (ETA end of the week to land the PR, and I'm not sure when exactly it will be merged).
This is happening even with non-parametrized thunks. The problem is that the unwrapping is expanding to be the thunk function itself (the thing that receives dispatch, getState, etc from the middleware) instead of being the action creator returning the thunk's result.
This does cause invoking the function to have the correct return type (hooray), but it has the wrong parameter types.

declare const mapped: WithThunkActionCreators<typeof dispatch>
const dispatch = {
foo(): ThunkAction<true, void, void, AnyAction> {
return dispatch => {
dispatch({ type: 'foo' })
return true
}
}
}
const result = mapped.foo()
This is happening even with non-parametrized thunks. The problem is that the unwrapping is expanding to be the thunk function itself (the thing that receives dispatch, getState, etc from the store) instead of being the action creator returning the thunk's result.
Thank you very much for clarification. It's definitely an oversight from my side.
This does cause invoking the function to have the correct return type (hooray), but it has the wrong parameter types.
It's because the initial concern in my codebase was about return types, and I focused too much on it, forgetting about proper types for the parameters. I'll address that as quickly as possible.
I think we need TS 3.1 features to correctly type the result; it should be something like
type BoundThunkCreator<T extends (...args: any[]) => any> = T extends (
...args: infer A
) => (dispatch: (action: any) => any, getState: () => any, extra: any) => infer R
? (...args: A) => R
: T
Thanks @surgeboris :-)
Should work in @types/[email protected]
@rhys-vdw can you check and (if issue is resolved) close this issue?
This issue still exists, for async functions/thunks provided to mapDispatchToProps the outer function is omitted.
getting :
Type '() => Promise<Action<any>>' is not assignable to type '() => (
dispatch: ThunkDispatch<FullState, undefined, AnyAction>
) => Promise<Action<any>>'
Most helpful comment
I think we need TS 3.1 features to correctly type the result; it should be something like