typesafe-actions icon indicating copy to clipboard operation
typesafe-actions copied to clipboard

make isActionOf accept an AsyncAction

Open lrdxg1 opened this issue 6 years ago • 9 comments

Is your feature request related to a problem?

I would like to know if an action is coming from an action creator created using createAsyncAction, in an elegant and typesafe way.

Describe a solution you'd like

I would like to have isActionOf accept an object created by createAsyncAction. Or maybe have a isAsyncActionOf.

Describe alternatives you've considered

For now I have to type the three possible action creators in an array provided to isActionOf

const fetchTodos = createAsyncAction(
  'todos/fetch_request',
  'todos/fetch_success',
  'todos/fetch_failure'
)< void, Todo[], CustomError >();

const todoAsyncActions = [ fetchTodos.request, fetchTodos.failure, fetchTodos.success ];

let requestName;
if (isActionOf(todoAsyncActions , action)) {
  requestName = 'todos';
}

Here is something I did try (but cannot provide type safety of the payload)

export type SimpleAsyncAction = { getType: () => string };

export type AsyncActionStates = 'request' | 'failure' | 'success';

export type SimpleAsyncActions = {
  [k in AsyncActionStates]: SimpleAsyncAction
};

export const isFromAsyncAction = (asyncActions: SimpleAsyncActions) => (
  action: FluxStandardAction,
) =>
  Object.values(asyncActions).some(
    (asyncAction) => asyncAction.getType() === action.type,
  );

lrdxg1 avatar Dec 20 '18 13:12 lrdxg1

I have a plan for this feature. At the moment isActionOf has really complex overloads and it would introduce too much complexity if we would want to add type inference logic there to handle async actions as well. Separate utility is also not optimal as I don't want to create special API for any particular creator type, I would rather prefer more generic utility.

I think the best solution would be to refactor isActionOf to use tuples, that would simplify overloads and would allow to extend isActionOf to handle async actions as well.

const fetchTodos = createAsyncAction(
  'todos/fetch_request',
  'todos/fetch_success',
  'todos/fetch_failure'
)< void, Todo[], CustomError >();

if (isActionOf(fetchTodos, action)) {
	action; // requestAction | successAction | errorAction
}

piotrwitek avatar Dec 31 '18 01:12 piotrwitek

Question maybe related to this, is it possible to create an ActionType that only contains the AsyncActions?

Ex: Today we do export type RootAction = ActionType<typeof rootAction>; which contains all actions regardless of async action or not.

could we have export type AsyncAction = AsyncActionType<typeof rootAction>; which returns typings for only the Async ones?

pachuka avatar Feb 15 '19 15:02 pachuka

@pachuka I haven't try but I guess yes, create a feature request and me or someone else might pick it up. It would need to add tag property to the async-action-creator and it should work like a charm.

piotrwitek avatar Feb 15 '19 15:02 piotrwitek

@issuehunt has funded $100.00 to this issue.


IssueHuntBot avatar Apr 12 '19 01:04 IssueHuntBot

hi @piotrwitek, I'd like to work on this issue, is this still relevant? I mean, if no one else have started work on this?

kononenko-a-m avatar Feb 05 '20 18:02 kononenko-a-m

Hey @kononenko-a-m, I've been working on it and have made some progress, although I hit the blocker because of design limitations, and I wanted to rethink the design but I don't remember all the details now. Are you familiar with the async action implementation and tried to implement it? It won't be easy.

piotrwitek avatar Feb 05 '20 22:02 piotrwitek

@piotrwitek In general, yes.

so you want to have mentioned by you behaviour work:

const fetchTodos = createAsyncAction(
  'todos/fetch_request',
  'todos/fetch_success',
  'todos/fetch_failure'
)< void, Todo[], CustomError >();

if (isActionOf(fetchTodos, action)) {
	action; // requestAction | successAction | errorAction
}

right?

kononenko-a-m avatar Feb 06 '20 19:02 kononenko-a-m

the simplest solution which I see for this would be defining several overloading for isActionOf, like this:

export function isActionOf<
  TType1 extends TypeConstant,
  TPayload1 extends any,
  TArgs1 extends any[],
  TType2 extends TypeConstant,
  TPayload2 extends any,
  TArgs2 extends any[],
  TType3 extends TypeConstant,
  TPayload3 extends any,
  TArgs3 extends any[]
  >(asyncAction:
   ReturnType<
    AsyncAction<
        TType1, unknown, unknown, TArgs1,
        TType2, unknown, unknown, TArgs2,
        TType3, unknown, unknown, TArgs3,
        never, unknown, unknown, never
      >
    >,
    action: { type: string }): action is 
  | PayloadAction<TType1, TPayload1> 
  | PayloadAction<TType2, TPayload2>
  | PayloadAction<TType3, TPayload3>

this gives desired result, but it wouldn't be very elegant. not sure if it's ok for you?

kononenko-a-m avatar Feb 06 '20 20:02 kononenko-a-m

Hey! Is this issue still up for grabs(Issuehunt bouny)? Thanks! @piotrwitek @kononenko-a-m

mighty-phoenix avatar Mar 23 '22 06:03 mighty-phoenix