dphilipson / typescript-fsa-reducers

Fluent syntax for defining typesafe reducers on top of typescript-fsa.
MIT License
220 stars 16 forks source link

Allow assigning multiple action types to the same handler #13

Closed dphilipson closed 7 years ago

dphilipson commented 7 years ago

Currently, if several action types should have the same handler, the only way to do it is with repetition:

reducerWithInitialState(foo)
    .case(DoOneThing, handler)
    .case(DoOtherThing, handler)
    .case(DoThirdThing, handler)

It would be preferable to have something like

reducerWithInitialState(foo)
    .cases([DoOneThing, DoAnotherThing, DoThirdThing], handler);

The bit of trickiness here is that the handler will receive a payload which may be any of the payload types of the input actions. To specify this under the type system, we need an overload for each number of arguments:

    cases<P1>(
        actionCreators: [ActionCreator<P1>],
        handler: Handler<InS, OutS, P1>,
    ): ReducerBuilder<InS, OutS>;
    cases<P1, P2>(
        actionCreators: [ActionCreator<P1>, ActionCreator<P2>],
        handler: Handler<InS, OutS, P1 | P2>,
    ): ReducerBuilder<InS, OutS>;
    cases<P1, P2, P3>(
        actionCreators: [
            ActionCreator<P1>,
            ActionCreator<P2>,
            ActionCreator<P3>
        ],
        handler: Handler<InS, OutS, P1 | P2 | P3>,
    ): ReducerBuilder<InS, OutS>;
    cases<P1, P2, P3, P4>(
        actionCreators: [
            ActionCreator<P1>,
            ActionCreator<P2>,
            ActionCreator<P3>,
            ActionCreator<P4>
        ],
        handler: Handler<InS, OutS, P1 | P2 | P3 | P4>,
    ): ReducerBuilder<InS, OutS>;
    // ... and so on.

This means that the function will only be typechecked if it has a number of arguments up to the number of overloads we define. Still, this is probably worth it if we assume most people won't want to put the same handler on more than eight or so different action types.

One remaining question is how to deal with a number of action types greater than the limit. We have three options:

As an alternative to variadic arguments, we could instead use chaining once again, which would look something like:

reducerWithInitialState(foo)
    .anyOf()
    .case(DoOneThing)
    .case(DoAnotherThing)
    .case(DoAThirdThing)
    .handleWith(handler)
    .case(BackToNormal, handleBackToNormal);

This is nicer as far as the type system is concerned (it works for any number of arguments, and avoids many variadic overloads), but is a fair bit harder to understand.

I'm going to think over this a bit then implement one of the solutions. Feel free to chime in if you have opinions about this.

dphilipson commented 7 years ago

Fixed by https://github.com/dphilipson/typescript-fsa-reducers/issues/15.