cartant/ts-action

More than 3 actions in reducer's on statement.

Closed this issue · 5 comments

I stacked with the problem that reducer is not doing autoresolve of handler arguments types if i will send there more than 3 action in one queue.

image
image

I also found hardcoded that you accepts only 3 args: export declare function on<C1 extends ActionCreator, C2 extends ActionCreator, C3 extends ActionCreator, S>(creator1: C1, creator2: C2, creator3: C3, reducer: OnReducer<S, [C1, C2, C3]>): On<S>;
Is this possible to make it more generic?

I really like how this library helps solving redux boilerplate code problem and i want continue using it.

Yeah. I can do something about this. It's the way it is because I simply could no get TypeScript to do what I wanted. I can see, now, what the problem is and that it's not something in TypeScript that is ever going to change.

I don't just want to keep adding overload signatures. That's what's done in NgRx's createReducer - which is based on ts-action. It has ten overload signatures. I would like a proper solution to this.

I have a few ideas for a solution. And I have a question that's related: have you ever had cause to use the union function that's in this package? I'm considering tweaking it to use in the solution. It would be an easily-fixed breaking change - for users of union - and I'm wondering whether people use it often. I suppose that if you are using the reducer function union is not something you really need?

Thanks a lot for the quick reply.
I know about this typescript problem too. And i think that helper function that helps us to combine many action will be great solution. Shoult it be something like that?
image

Yeah. Close. It will look like this:

on(...union(foo, bar), (state, action) => {})

The fundamental problem is that TypeScript will not infer a union of functions and the action creators - foo and bar - are functions. I will change the implementation of union so that it returns an array of action creators with the types changed so that they are not functions - the implementation of on will be the same. There will just be some type trickery.

Having heaps of overload signatures is something I really don't like. I think this will be a reasonable solution.

I will also change the current catch-all signature so that it will work without error. It will just lose the narrowing.

I've pushed the changes. If you are interested in the usage, have a look at this test:

it("should support reducers with multiple actions spread from union", () => {
type State = string[];
const initialState: State = [];
const fooBarReducer = reducer(
initialState,
on(...union(foo, bar, baz, boo), (state, { type }) => [
...state,
type
])
);
expect(fooBarReducer).to.be.a("function");
let state = fooBarReducer(undefined, { type: "UNKNOWN" });
expect(state).to.deep.equal([]);
state = fooBarReducer(state, foo({ foo: 42 }));
expect(state).to.deep.equal(["[foobar] FOO"]);
state = fooBarReducer(state, bar({ bar: 54 }));
expect(state).to.deep.equal(["[foobar] FOO", "[foobar] BAR"]);
});

If you are interested in the TypeScript behaviours that need to be worked around, have a look at these experiments:

https://github.com/cartant/ts-action/blob/master/packages/ts-action/source/reducer-experiment-spec.ts

I've make some changes to the README and will publish a new version later.

It would be simpler to just use an array of action creators - rather than a rest parameter - but mixing array and non-array signatures is something I wanted to avoid. And, who knows, maybe the TypeScript behaviour might change, some day, to better suit this.

Another alternative could have been to use an API something like this:

  on(foo, bar, baz, boo).reduce((state, action) => state)

Doing so would have made things much simpler.

v11.0.0 has been published and you can now use union to pass as many action creators as you like to on: https://github.com/cartant/ts-action/blob/master/packages/ts-action/README.md#reducer