From 3bfedf56769bb98236483bdc415a393c49228155 Mon Sep 17 00:00:00 2001 From: Mikolaj Klaman Date: Tue, 21 May 2019 19:53:35 +0200 Subject: [PATCH] fix: type-safety of action creators - enforce correct number of arguments using fuction overloads - update README closes #27 and #28 --- README.md | 12 +++++++++--- src/create-action.ts | 26 ++++++++++++++++++++++---- src/example.ts | 8 ++++++-- 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 020bdec..0a1ec8c 100644 --- a/README.md +++ b/README.md @@ -21,8 +21,8 @@ import { createAction, handleAction, reduceReducers } from 'redux-ts-utils'; // Actions -const increment = createAction('increment'); -const decrement = createAction('decrement'); +const increment = createAction('increment'); +const decrement = createAction('decrement'); const add = createAction('add'); const override = createAction('override'); @@ -62,7 +62,11 @@ store.dispatch(increment()); store.dispatch(increment()); store.dispatch(increment()); store.dispatch(decrement()); +// store.dispatch(decrement(1)); <-- TypeError: Expected 0 arguments, but got 1. + store.dispatch(add(10)); +// store.dispatch(add()); <-- TypeError: Expected 1 arguments, but got 0. + console.log('Final count!', store.getState().counter); // 12 ``` @@ -104,7 +108,9 @@ properties: `type` (a `string`) and `payload` (typed as `T`). Typically it is best to use the simplest signature for this function: ```ts -const myActionCreator = createAction('MY_ACTION'); +const myVoidActionCreator = createAction('MY_VOID_ACTION'); + +const myPayloadActionCreator = createAction('MY_PAYLOAD_ACTION'); ``` The action creator function will be typed to take whatever you provide as a diff --git a/src/create-action.ts b/src/create-action.ts index 49c568f..50627f4 100644 --- a/src/create-action.ts +++ b/src/create-action.ts @@ -11,16 +11,32 @@ export interface TsActionCreator

{ (...args: A): TsAction; type: string; } +interface TsIdentityPayloadActionCreator

{ + (payload: P): TsAction; + type: string; +} +interface TsVoidActionCreator { + (): TsAction; + type: string; +} export type PayloadCreator = (...args: A) => P; export const identity = (...arg: T): T[0] => arg[0]; -// eslint-disable-next-line arrow-parens -export default ( + +function createAction(type: string): TsVoidActionCreator; +function createAction

(type: string): TsIdentityPayloadActionCreator

; +function createAction( + type: string, + pc: PayloadCreator, + mc?: PayloadCreator, +): TsActionCreator; + +function createAction ( type: string, pc: PayloadCreator = identity, mc?: PayloadCreator, -): TsActionCreator => { +): TsVoidActionCreator | TsIdentityPayloadActionCreator

| TsActionCreator { // Continue with creating an action creator const ac = (...args: A): TsAction => { const payload = pc(...args); @@ -42,4 +58,6 @@ export default ( ac.toString = () => type; return ac as TsActionCreator; -}; +} + +export default createAction; diff --git a/src/example.ts b/src/example.ts index d56b24f..947d56f 100644 --- a/src/example.ts +++ b/src/example.ts @@ -5,8 +5,8 @@ import { createAction, handleAction, reduceReducers } from '.'; // Actions -const increment = createAction('increment'); -const decrement = createAction('decrement'); +const increment = createAction('increment'); +const decrement = createAction('decrement'); const add = createAction('add'); const override = createAction('override'); @@ -46,7 +46,11 @@ store.dispatch(increment()); store.dispatch(increment()); store.dispatch(increment()); store.dispatch(decrement()); +// store.dispatch(decrement(1)); <-- TypeError: Expected 0 arguments, but got 1. + store.dispatch(add(10)); +// store.dispatch(add()); <-- TypeError: Expected 1 arguments, but got 0. + console.log('Final count!', store.getState().counter); assert(store.getState().counter === 12);