From 91c49fa475acf45957a6424b67bdcfc9044e0e22 Mon Sep 17 00:00:00 2001 From: Richard Honor Date: Mon, 5 Nov 2018 10:30:44 +0000 Subject: [PATCH 1/3] Add overload for bindActionCreators --- index.d.ts | 22 +++++++++++++++++++++- package-lock.json | 6 +++--- package.json | 2 +- test/typescript.ts | 45 +++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/index.d.ts b/index.d.ts index 87fbcd6..fdf66b9 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,10 @@ -import { Middleware, Action, AnyAction } from "redux"; +import { + Action, + ActionCreatorsMapObject, + AnyAction, + Dispatch, + Middleware, +} from 'redux'; export interface ThunkDispatch { (thunkAction: ThunkAction): R; @@ -18,3 +24,17 @@ declare const thunk: ThunkMiddleware & { } export default thunk; + +/** + * Redux behaviour changed by middleware, so overloads here + */ +declare module 'redux' { + /** + * Overload for bindActionCreators redux function, returns expects responses + * from thunk actions + */ + function bindActionCreators>( + actionCreators: M, + dispatch: Dispatch, + ): { [N in keyof M]: ReturnType extends ThunkAction ? (...args: Parameters) => ReturnType> : M[N] } +} diff --git a/package-lock.json b/package-lock.json index 8d7cd8f..91f60c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3652,9 +3652,9 @@ "dev": true }, "typescript": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.6.2.tgz", - "integrity": "sha1-PFtv1/beCRQmkCfwPAlGdY92c6Q=", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", + "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", "dev": true }, "typings-tester": { diff --git a/package.json b/package.json index b89fca1..b845735 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "mocha": "^2.2.5", "redux": "~4.0.0", "rimraf": "^2.5.2", - "typescript": "~2.6.2", + "typescript": "^3.1.6", "typings-tester": "^0.3.1", "webpack": "^1.12.14" } diff --git a/test/typescript.ts b/test/typescript.ts index 162877b..573b4af 100644 --- a/test/typescript.ts +++ b/test/typescript.ts @@ -1,5 +1,14 @@ -import { createStore, applyMiddleware } from 'redux'; -import thunk, { ThunkAction, ThunkMiddleware, ThunkDispatch } from '../index'; +import { + applyMiddleware, + bindActionCreators, + createStore, +} from 'redux'; + +import thunk, { + ThunkAction, + ThunkDispatch, + ThunkMiddleware, +} from '../index'; type State = { foo: string; @@ -50,6 +59,34 @@ function anotherThunkAction(): ThunkResult { } } +function promiseThunkAction(): ThunkResult> { + return async (dispatch, getState) => { + dispatch({ type: 'FOO' }); + return false; + } +} + +const standardAction = () => ({ type: 'FOO' }); + +// test that bindActionCreators correctly returns actions responses of ThunkActions +// also ensure standard action creators still work as expected +const actions = bindActionCreators({ + anotherThunkAction, + promiseThunkAction, + standardAction, +}, store.dispatch); + +actions.anotherThunkAction() === 'hello'; +// typings:expect-error +actions.anotherThunkAction() === false; +actions.promiseThunkAction().then((res) => console.log(res)); +// typings:expect-error +actions.promiseThunkAction().prop; +actions.standardAction().type; +// typings:expect-error +actions.standardAction().other; + + store.dispatch({ type: 'FOO' }); // typings:expect-error store.dispatch({ type: 'BAR' }) @@ -85,5 +122,5 @@ const callDispatchAsync_specificActions = (dispatch: ThunkDispatch) => { const asyncThunk = (): any => () => ({} as Promise); dispatch(asyncThunk()) // result is any - .then(() => console.log('done')) -} \ No newline at end of file + .then(() => console.log('done')) +} From d40fabad7b4bb4e65d3fd19fae123a3a7de35d31 Mon Sep 17 00:00:00 2001 From: Richard Honor Date: Thu, 22 Nov 2018 11:27:16 +0000 Subject: [PATCH 2/3] Add ThunkActionDispatch type to use when declaring interfaces --- index.d.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/index.d.ts b/index.d.ts index fdf66b9..dd11ea4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -17,6 +17,15 @@ export type ThunkAction = ( extraArgument: E ) => R; +/** + * Takes a ThunkAction and returns a function signature which matches how it would appear when processed using + * bindActionCreators + * + * @template T ThunkAction to be wrapped + */ +export type ThunkActionDispatch> = (...args: Parameters) + => ReturnType>; + export type ThunkMiddleware = Middleware, S, ThunkDispatch>; declare const thunk: ThunkMiddleware & { From 6ffd8dbcd1a5e75e6ee3eb3c05d96cad6fa0aec9 Mon Sep 17 00:00:00 2001 From: Richard Honor Date: Mon, 3 Dec 2018 09:14:59 +0000 Subject: [PATCH 3/3] Fix ThunkActionDispatch: generic should expect to extend a function, not just the return type --- index.d.ts | 2 +- test/typescript.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/index.d.ts b/index.d.ts index dd11ea4..441f0b2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -23,7 +23,7 @@ export type ThunkAction = ( * * @template T ThunkAction to be wrapped */ -export type ThunkActionDispatch> = (...args: Parameters) +export type ThunkActionDispatch ThunkAction> = (...args: Parameters) => ReturnType>; export type ThunkMiddleware = Middleware, S, ThunkDispatch>; diff --git a/test/typescript.ts b/test/typescript.ts index 573b4af..9dad756 100644 --- a/test/typescript.ts +++ b/test/typescript.ts @@ -6,6 +6,7 @@ import { import thunk, { ThunkAction, + ThunkActionDispatch, ThunkDispatch, ThunkMiddleware, } from '../index'; @@ -68,14 +69,22 @@ function promiseThunkAction(): ThunkResult> { const standardAction = () => ({ type: 'FOO' }); +interface ActionDispatchs { + anotherThunkAction: ThunkActionDispatch; + promiseThunkAction: ThunkActionDispatch; + standardAction: typeof standardAction; +} + // test that bindActionCreators correctly returns actions responses of ThunkActions // also ensure standard action creators still work as expected -const actions = bindActionCreators({ +const actions: ActionDispatchs = bindActionCreators({ anotherThunkAction, promiseThunkAction, standardAction, }, store.dispatch); + + actions.anotherThunkAction() === 'hello'; // typings:expect-error actions.anotherThunkAction() === false;