Skip to content

Commit

Permalink
Tags types
Browse files Browse the repository at this point in the history
  • Loading branch information
rjz committed Mar 7, 2018
1 parent f955226 commit 67718f4
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 31 deletions.
68 changes: 46 additions & 22 deletions src/actions/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,64 @@ type Q<T> = { request: T }
type S<T> = { response: T }
type E = { error: string }

type Value = { value: number }
export type Value = { value: number }

type LoadCountAction =
({ type: 'LOAD_COUNT_REQUEST' } & Q<{}>)
| ({ type: 'LOAD_COUNT_SUCCESS' } & Q<{}> & S<Value>)
| ({ type: 'LOAD_COUNT_ERROR' } & Q<{}> & E)
type ThunkAction<TQ, TS, TE, _Q, _S> =
({ type: TQ } & Q<_Q>)
| ({ type: TS } & Q<_Q> & S<_S>)
| ({ type: TE } & Q<_Q> & E)

type SaveCountAction =
({ type: 'SAVE_COUNT_REQUEST' } & Q<Value>)
| ({ type: 'SAVE_COUNT_SUCCESS' } & Q<Value> & S<{}>)
| ({ type: 'SAVE_COUNT_ERROR' } & Q<Value> & E)
export type LoadCount = ThunkAction<
'LOAD_COUNT_REQUEST',
'LOAD_COUNT_SUCCESS',
'LOAD_COUNT_ERROR',
{},
Value
>

export type SaveCount = ThunkAction<
'SAVE_COUNT_REQUEST',
'SAVE_COUNT_SUCCESS',
'SAVE_COUNT_ERROR',
Value,
{}
>

export type Action =
LoadCountAction
| SaveCountAction
LoadCount
| SaveCount
| { type: 'INCREMENT_COUNTER', delta: number }
| { type: 'RESET_COUNTER' }

type _T = Action['type']

type ThunkActionGroup<TQ extends _T, TS extends _T, TE extends _T, _Q, _S> =
({ type: TQ } & Q<_Q>)
| ({ type: TS } & Q<_Q> & S<_S>)
| ({ type: TE } & Q<_Q> & E)
// TypeScript won't narrow a `ThunkAction` union directly, but
// we can help it out by tagging the three permutations.
export const asReq = <TQ extends _T>(type: TQ) =>
<_Q>(request: _Q) =>
({ type, request })

export const asRes = <TS extends _T>(type: TS) =>
<_Q, _S>(request: _Q, response: _S) =>
({ type, request, response })

export const asErr = <TE extends _T>(type: TE) =>
<_Q>(request: _Q, error: string) =>
({ type, request, error })

type Dispatch<A> = (a: A) => A
type Thunk<Q, S> = (request: Q) => Promise<S>

export const createThunkAction = <Q, S, TQ extends _T, TS extends _T, TE extends _T>
(fn: Thunk<Q, S>, tq: TQ, ts: TS, te: TE) =>
(request: Q) =>
(dispatch: Dispatch<ThunkActionGroup<TQ, TS, TE, Q, S>>) => {
dispatch({ type: tq, request })
type ReqCreator<A, _Q> = (q: _Q) => A
type ResCreator<A, _Q, _S> = (q: _Q, s: _S) => A
type ErrCreator<A, _Q> = (q: _Q, e: string) => A

export const dispatcher = <_Q, _S>(fn: Thunk<_Q, _S>) =>
<A extends Action>(tq: ReqCreator<A, _Q>, ts: ResCreator<A, _Q, _S>, te: ErrCreator<A, _Q>) =>
(request: _Q) =>
(dispatch: Dispatch<A>) => {
dispatch(tq(request))
fn(request)
.then(response => dispatch({ type: ts, request, response }))
.catch(err => dispatch({ type: te, request, error: err.message }))
.then(response => dispatch(ts(request, response)))
.catch(err => dispatch(te(request, err.message)))
}
23 changes: 14 additions & 9 deletions src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@ import { api } from '../api'

import {
Action,
createThunkAction,
LoadCount,
SaveCount,
dispatcher,
asReq,
asRes,
asErr,
} from './action'

export { Action }
Expand All @@ -16,12 +21,12 @@ export const resetCounter = (): Action => ({
type: 'RESET_COUNTER',
})

export const saveCount = createThunkAction(api.save,
'SAVE_COUNT_REQUEST',
'SAVE_COUNT_SUCCESS',
'SAVE_COUNT_ERROR')
export const loadCount = dispatcher(api.load)<LoadCount>(
asReq('LOAD_COUNT_REQUEST'),
asRes('LOAD_COUNT_SUCCESS'),
asErr('LOAD_COUNT_ERROR'))

export const loadCount = createThunkAction(api.load,
'LOAD_COUNT_REQUEST',
'LOAD_COUNT_SUCCESS',
'LOAD_COUNT_ERROR')
export const saveCount = dispatcher(api.save)<SaveCount>(
asReq('SAVE_COUNT_REQUEST'),
asRes('SAVE_COUNT_SUCCESS'),
asErr('SAVE_COUNT_ERROR'))

0 comments on commit 67718f4

Please sign in to comment.