The problem is the call to bindActionCreators in mapDispatchToProps. At runtime bindActionCreators basically transforms this (id: string) => (dispatch: Dispatch<TActions>) => Promise<string>; into this (id: string) => Promise<string>;, but the type for bindActionCreators does not reflect this transformation. This is probably due to the fact that to accomplish this you would need conditional types which until recently were not available.
If we look at this sample usage from the redux repo, we see that they accomplish the transformation by specifying the types of the functions explicitly:
const boundAddTodoViaThunk = bindActionCreators<
ActionCreator<AddTodoThunk>,
ActionCreator<AddTodoAction>
>(addTodoViaThunk, dispatch)
We could do the same in your code, referencing existing types, but this hurts type safety as there is no check that fetchPosts in the two types will be correctly typed:
const mapDispatchToProps = (dispatch: Dispatch<TActions>): Partial<IProps> =>
bindActionCreators<{ fetchPosts: typeof fetchPosts }, Pick<IProps, 'fetchPosts'>>(
{
fetchPosts
},
dispatch
);
Or we could use a type assertion since the above method does not really offer any safety anyway:
const mapDispatchToProps2 = (dispatch: Dispatch<TActions>) =>
bindActionCreators({
fetchPosts: fetchPosts as any as ((id: string) => Promise<string>)
}, dispatch );
For a truly type safe way to do this we need to use typescript 2.8 and conditional types with a helper function. We can type bindActionCreators the way it should, and automatically infer the correct type for the resulting creators:
function mybindActionCreators<M extends ActionCreatorsMapObject>(map: M, dispatch: Dispatch<TActions>) {
return bindActionCreators<M, { [P in keyof M] : RemoveDispatch<M[P]> }>(map, dispatch);
}
const mapDispatchToProps = (dispatch: Dispatch<TActions>) =>
mybindActionCreators(
{
fetchPosts
},
dispatch
);
// Helpers
type IsValidArg<T> = T extends object ? keyof T extends never ? false : true : true;
type RemoveDispatch<T extends Function> =
T extends (a: infer A, b: infer B, c: infer C, d: infer D, e: infer E, f: infer F, g: infer G, h: infer H, i: infer I, j: infer J) => (dispatch: Dispatch<any>) => infer R ? (
IsValidArg<J> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) => R :
IsValidArg<I> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) => R :
IsValidArg<H> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) => R :
IsValidArg<G> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F, g: G) => R :
IsValidArg<F> extends true ? (a: A, b: B, c: C, d: D, e: E, f: F) => R :
IsValidArg<E> extends true ? (a: A, b: B, c: C, d: D, e: E) => R :
IsValidArg<D> extends true ? (a: A, b: B, c: C, d: D) => R :
IsValidArg<C> extends true ? (a: A, b: B, c: C) => R :
IsValidArg<B> extends true ? (a: A, b: B) => R :
IsValidArg<A> extends true ? (a: A) => R :
() => R
) : T;
bindActionCreatorsis wrong, since at runtime if you pass it a function that returns a function, the result is a function that will have the second function invoked automatically withdispatch, but the typings don't reflect this, they just return a function with the same type. But I am not knowledgeable enough with redux to state this for surebindActionCreators( { fetchPosts: fetchPosts as any as ((id: string) => Promise<string>) }, dispatch );