Add ability to easily create redux middleware for async actions
This commit is contained in:
parent
fa27dda530
commit
dca9691d7d
@ -6,11 +6,11 @@ import bodyParser from 'body-parser'
|
||||
import express from 'express'
|
||||
import {AddressInfo} from 'net'
|
||||
import {Server} from 'http'
|
||||
import {createReduxClient} from './redux'
|
||||
import {createReduxClient, createReducer} from './redux'
|
||||
import {createRemoteClient} from './remote'
|
||||
import {jsonrpc} from './express'
|
||||
import {keys} from 'ts-transformer-keys'
|
||||
import {TActionCreators, TAllActions} from './types'
|
||||
import {TPendingActions, TAllActions} from './types'
|
||||
|
||||
describe('createReduxClient', () => {
|
||||
|
||||
@ -73,9 +73,33 @@ describe('createReduxClient', () => {
|
||||
// type R<T> = T extends (...args: any[]) => infer RV ? RV : never
|
||||
|
||||
type Client = typeof client
|
||||
type ActionCreators = TActionCreators<typeof client>
|
||||
type ActionCreators = TPendingActions<typeof client>
|
||||
type AllActions = TAllActions<typeof client>
|
||||
|
||||
const defaultState = {
|
||||
sum: 1,
|
||||
}
|
||||
|
||||
const create = createReducer('myService', defaultState)
|
||||
<typeof client>((state, action) => {
|
||||
switch (action.method) {
|
||||
case 'add':
|
||||
const r1: number = action.payload
|
||||
return state
|
||||
case 'addAsync':
|
||||
const r2: number = action.payload
|
||||
return state
|
||||
case 'addStringsAsync':
|
||||
const r3: string = action.payload
|
||||
return state
|
||||
case 'addWithContext':
|
||||
const r4: number = action.payload
|
||||
return state
|
||||
default:
|
||||
return state
|
||||
}
|
||||
})
|
||||
|
||||
function handleAction(state: any, action: AllActions) {
|
||||
if (action.type !== 'myService') {
|
||||
return
|
||||
@ -99,9 +123,6 @@ describe('createReduxClient', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// type Values<T> = T[keyof T]
|
||||
// type C = ReturnType<Values<typeof client>>
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import {TAsyncified, TReduxed} from './types'
|
||||
import {
|
||||
IReduxed,
|
||||
TAsyncified,
|
||||
TReduxed,
|
||||
TResolvedActions,
|
||||
TAllActions,
|
||||
} from './types'
|
||||
import {createRemoteClient} from './remote'
|
||||
|
||||
export function createReduxClient<T, ActionType extends string>(
|
||||
@ -20,3 +26,26 @@ export function createReduxClient<T, ActionType extends string>(
|
||||
|
||||
return service as TReduxed<T, ActionType>
|
||||
}
|
||||
|
||||
export const createReducer = <ActionType extends string, State>(
|
||||
actionType: ActionType,
|
||||
defaultState: State,
|
||||
) => <R extends IReduxed<ActionType>>(
|
||||
handleAction: (state: State, action: TResolvedActions<R>) => State,
|
||||
) => (state: State = defaultState, action?: TAllActions<R>): State => {
|
||||
if (!action) {
|
||||
return state
|
||||
}
|
||||
if (action.type !== actionType) {
|
||||
return state
|
||||
}
|
||||
if (action.status === 'pending') {
|
||||
// TODO handle loading
|
||||
return state
|
||||
}
|
||||
if (action.status === 'rejected') {
|
||||
// TODO handle rejected
|
||||
return state
|
||||
}
|
||||
return handleAction(state, action)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ type ArgumentTypes<T> =
|
||||
T extends (...args: infer U) => infer R ? U : never
|
||||
type RetType<T> = T extends (...args: any[]) => infer R ? R : never
|
||||
type UnwrapHOC<T> = T extends (...args: any[]) => infer R ? R : T
|
||||
type UnwrapPromise<T> = T extends Promise<infer V> ? V : T
|
||||
type RetProm<T> = T extends Promise<any> ? T : Promise<T>
|
||||
type PromisifyReturnType<T> = (...a: ArgumentTypes<T>) =>
|
||||
RetProm<UnwrapHOC<RetType<T>>>
|
||||
@ -12,19 +13,13 @@ export type TAsyncified<T> = {
|
||||
[K in keyof T]: PromisifyReturnType<T[K]>
|
||||
}
|
||||
|
||||
export interface IReduxed<ActionType extends string> {
|
||||
[key: string]: (...a: any[]) => IRPCPendingAction<any, ActionType, typeof key>
|
||||
}
|
||||
|
||||
export type TReduxed<T, ActionType extends string> = {
|
||||
// [K in keyof T]: (...a: ArgumentTypes<T[K]>) => {
|
||||
// type: ActionType
|
||||
// payload: RetProm<UnwrapHOC<RetType<T[K]>>>
|
||||
// method: K
|
||||
// status: 'pending'
|
||||
// }
|
||||
// [K in keyof T]: (...a: ArgumentTypes<T[K]>) =>
|
||||
// IPendingAction<RetProm<UnwrapHOC<RetType<T[K]>>>, ActionType> & {
|
||||
// method: K,
|
||||
// }
|
||||
[K in keyof T]: (...a: ArgumentTypes<T[K]>) =>
|
||||
IRPCPendingAction<UnwrapHOC<RetType<T[K]>>, ActionType, K>
|
||||
IRPCPendingAction<UnwrapPromise<UnwrapHOC<RetType<T[K]>>>, ActionType, K>
|
||||
}
|
||||
|
||||
export interface IRPCPendingAction<
|
||||
@ -45,6 +40,13 @@ export interface IRPCRejectedAction<
|
||||
method: Method
|
||||
}
|
||||
|
||||
export type TRPCAction<
|
||||
T, ActionType extends string, Method extends string | number | symbol
|
||||
> =
|
||||
IRPCPendingAction<T, ActionType, Method>
|
||||
| IRPCResolvedAction<T, ActionType, Method>
|
||||
| IRPCRejectedAction<ActionType, Method>
|
||||
|
||||
export type TResolved<A> =
|
||||
A extends IRPCPendingAction<infer T, infer ActionType, infer Method>
|
||||
? IRPCResolvedAction<T, ActionType, Method>
|
||||
@ -55,7 +57,13 @@ export type TRejected<A> =
|
||||
? IRPCRejectedAction<ActionType, Method>
|
||||
: never
|
||||
|
||||
export type TPending<A> =
|
||||
A extends IRPCPendingAction<infer T, infer ActionType, infer Method>
|
||||
? IRPCPendingAction<T, ActionType, Method>
|
||||
: never
|
||||
|
||||
type Values<T> = T[keyof T]
|
||||
export type TActionCreators<T> = RetType<Values<T>>
|
||||
export type TAllActions<T> = TActionCreators<T>
|
||||
| TResolved<TActionCreators<T>> | TRejected<TActionCreators<T>>
|
||||
export type TPendingActions<T> = TPending<RetType<Values<T>>>
|
||||
export type TResolvedActions<T> = TResolved<TPendingActions<T>>
|
||||
export type TAllActions<T> = TPendingActions<T>
|
||||
| TResolvedActions<T> | TRejected<TPendingActions<T>>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user