UNPKG

1.75 kBPlain TextView Raw
1import createNextState, { isDraft } from 'immer'
2import type { EntityState, PreventAny } from './models'
3import type { PayloadAction } from '../createAction'
4import { isFSA } from '../createAction'
5import { IsAny } from '../tsHelpers'
6
7export function createSingleArgumentStateOperator<V>(
8 mutator: (state: EntityState<V>) => void
9) {
10 const operator = createStateOperator((_: undefined, state: EntityState<V>) =>
11 mutator(state)
12 )
13
14 return function operation<S extends EntityState<V>>(
15 state: PreventAny<S, V>
16 ): S {
17 return operator(state as S, undefined)
18 }
19}
20
21export function createStateOperator<V, R>(
22 mutator: (arg: R, state: EntityState<V>) => void
23) {
24 return function operation<S extends EntityState<V>>(
25 state: S,
26 arg: R | PayloadAction<R>
27 ): S {
28 function isPayloadActionArgument(
29 arg: R | PayloadAction<R>
30 ): arg is PayloadAction<R> {
31 return isFSA(arg)
32 }
33
34 const runMutator = (draft: EntityState<V>) => {
35 if (isPayloadActionArgument(arg)) {
36 mutator(arg.payload, draft)
37 } else {
38 mutator(arg, draft)
39 }
40 }
41
42 if (isDraft(state)) {
43 // we must already be inside a `createNextState` call, likely because
44 // this is being wrapped in `createReducer` or `createSlice`.
45 // It's safe to just pass the draft to the mutator.
46 runMutator(state)
47
48 // since it's a draft, we'll just return it
49 return state
50 } else {
51 // @ts-ignore createNextState() produces an Immutable<Draft<S>> rather
52 // than an Immutable<S>, and TypeScript cannot find out how to reconcile
53 // these two types.
54 return createNextState(state, runMutator)
55 }
56 }
57}