piotrwitek / typesafe-actions

Typesafe utilities for "action-creators" in Redux / Flux Architecture
https://codesandbox.io/s/github/piotrwitek/typesafe-actions/tree/master/codesandbox
MIT License
2.41k stars 98 forks source link

Is there a way to automatically generate PayloadAction type unions from an AsyncActionCreateBuilder instance to facilitate writing custom epic generic templates #212

Open dcs3spp opened 4 years ago

dcs3spp commented 4 years ago

Is your feature request related to a real problem or use-case?

I have written a generic epic that accepts an async action as a parameter:

export const genericHandler = <
  TRequestType extends string,
  TSuccessType extends string,
  TFailType extends string,
  TModel,
>(
  asyncAction: AsyncActionCreatorBuilder<
    [TRequestType, RequestPayload<TModel>],
    [TSuccessType, SuccessPayload<TModel>],
    [TFailType, FailurePayload<TModel>]
  >,
) => {
  const epic: Epic<
    // epic input action types union - manually specifying.....
    | PayloadAction<
        TRequestType,
        RequestPayload<TModel>,
      >
    | PayloadAction<TSuccessType, SuccessPayload<TModel>>
    | PayloadAction<TFailType, FailurePayload<TModel>>,
    // output action types union - manually specifying
    | PayloadAction<
        TRequestType,
        RequestPayload<TModel>
      >
    | PayloadAction<TSuccessType, SuccessPayload<TModel>>
    | PayloadAction<TFailType, FailurePayload<TModel>>,
    RootState,
    Services
  > = (action$, state$, { apiService }) => {
      // some source code ....
  }

The type of the aysncAction parameter is AsyncActionCreatorBuilder. In the redux-observable Epic<...> generic I manually specify the PayloadAction types for Input and Output actions in the stream.

Describe a solution including usage in code example

Is there any way to automatically generate the PayloadAction types to reduce complexity? Is it possible to derive the PayloadAction union types from AsyncActionCreatorBuilder to reduce complexity....

// imports for RequestPayload, SuccessPayload, FailurePayload etc.
export const customEpicCreator_A = <
  TRequestType extends string,
  TSuccessType extends string,
  TFailType extends string,
  TModel,
>(
  asyncAction: AsyncActionCreatorBuilder<
    [TRequestType, RequestPayload<TModel>],
    [TSuccessType, SuccessPayload<TModel>],
    [TFailType, FailurePayload<TModel>]
  >,
) => {
  const epic: Epic<
    // Payload type union automatically generated for input and output types from AsyncActionCreatorBuilder object
    GeneratePayloadActionTypes<asyncAction>,
    GeneratePayloadActionType<asyncAction>,
    RootState,
    Services
  > = (action$, state$, { apiService }) => {
      // some source code ....
  }

export const customEpicCreator_B = <
  TRequestType extends string,
  TSuccessType extends string,
  TFailType extends string,
  DifferentRequestObject,
  DifferentSuccessObject,
  DifferentFailObject,
>(
  asyncAction: AsyncActionCreatorBuilder<
    [TRequestType, DifferentRequestObject],
    [TSuccessType, DifferentSuccessObject],
    [TFailType, DifferentFailObject]
  >,
) => {
  const epic: Epic<
    // Payload type union automatically generated for input and output types from AsyncActionCreatorBuilder object
    GeneratePayloadActionTypes<asyncAction>,
    GeneratePayloadActionType<asyncAction>,
    RootState,
    Services
  > = (action$, state$, { apiService }) => {
      // some source code ....
  }

Who does this impact? Who is this for?

Typescript users

Describe alternatives you've considered (optional)

I tried using ActionType<typeof asyncAction> but this generates type

asyncAction: {
    request: PayloadActionCreator<TRequestType, RequestPayload<TModel>>;
    success: PayloadActionCreator<TSuccessType, SuccessPayload<TModel>>;
    failure: PayloadActionCreator<TFailType, FailPayload<TModel>>;
} 

I think this is because I am passing in the parameter type as an AsyncActionCreatorBuilder.

Additional context (optional)