Skip to content

Type error when trying to use spread syntax to pass matchers to isAnyOf or isAllOf #2209

@crhistianramirez

Description

@crhistianramirez

I have a big list of thunks that I want to observe in order to power loading indicators in a pseudo-global way and accomplishing this with matchers.

Instead of writing this which is tedious and error-prone:

const interestingThunks = [
   thunk1,
   thunk2
];
const isLoading = isAnyOf(thunk1.pending, thunk2.pending);
const isNotLoading = isAnyOf(thunk1.fulfilled, thunk2.fulfilled, thunk1.rejected, thunk2.rejected);

...

builder.addMatcher(isLoading, (state) => {
  state.loading= true;
});
builder.addMatcher(isNotLoading , (state) => {
  state.loading= false;
});

I would prefer to write this:

const interestingThunks = [
   thunk1,
   thunk2
];
const interestingPendingThunks = interestingThunks.map(thunk => thunk.pending);
const interestingFulfilledThunks = interestingThunks.map(thunk => thunk.fulfilled);
const isLoading = isAnyOf(...interestingPendingThunks );
const isNotLoading = isAnyOf(...interestingFulfilledThunks );

...

builder.addMatcher(isLoading, (state) => {
  state.loading= true;
});
builder.addMatcher(isNotLoading , (state) => {
  state.loading= false;
});

But I get the following type error when I try that:

A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)

Which seems to be happening because the type definition is:

export declare function isAnyOf<Matchers extends [Matcher<any>, ...Matcher<any>[]]>(...matchers: Matchers): (action: any) => action is ActionFromMatcher<Matchers[number]>;

There is a rest argument, but the first argument is a matcher. The workaround is to provide the first argument as normal to satisfy the condition and use the rest syntax for the others. This is a bit more verbose and not intuitive. My guess is that the first argument is defined as such so that it isn't possible to try and call isAnyOf without any arguments but maybe there is a way to have our cake and eat it as well.

Workaround:

const interestingThunks = [
   thunk1,
   thunk2
];
const interestingPendingThunks = interestingThunks.map(thunk => thunk.pending);
const interestingFulfilledThunks = interestingThunks.map(thunk => thunk.fulfilled);
const isLoading = isAnyOf(interestingPendingThunks[0], ...interestingPendingThunks );
const isNotLoading = isAnyOf(interestingFulfilledThunks[0], ...interestingFulfilledThunks );

...

builder.addMatcher(isLoading, (state) => {
  state.loading= true;
});
builder.addMatcher(isNotLoading , (state) => {
  state.loading= false;
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions