the-dr-lazy / deox

Functional Type-safe Flux Standard Utilities
https://deox.js.org
MIT License
206 stars 12 forks source link

Usage with redux-saga #53

Closed npeham closed 5 years ago

npeham commented 5 years ago

I use redux-saga. Currently, I have to define a type in addition to my action:

export type CreateUserRequestedAction = Action<
  ActionTypeKeys.CreateUserRequested,
  User
>;

export const createUserRequested = createAction(
  ActionTypeKeys.CreateUserRequested,
  resolve => (user: User): CreateUserRequestedAction => resolve(user),
);

I need this CreateUserRequestedAction type to use this later in my saga for the action parameter:

export function* userEditSaga(action: CreateUserRequestedAction) {
  try {
    // define type explicitly because of lack of return types of generators:
    // https://github.com/redux-saga/redux-saga/issues/1286#issuecomment-482866473
    const fetchedUser: User = yield call(userEditRequest, {
      firstName: 'john',
      lastName: 'doe',
    });
    yield put(createUserSucceeded(fetchedUser));
  } catch (error) {
    yield put(createUserFailed());
  }
}

export const userSagas = [
  takeLatest(ActionTypeKeys.CreateUserRequested, userEditSaga),
];

Is there a better solution for this or do I need this type to gain type-safety and autocompletion in my userEditSaga?

Thank you in advance!

Kind regards Nico

the-dr-lazy commented 5 years ago

Hi @npeham

There is a type helper called ActionType that can be used to infer action type that wrapped in given action creator/reducer.

https://github.com/thebrodmann/deox/blob/3fbdfd25500922410ceeff8d7eff95d1b6e5a780/src/types.ts#L35-L39

Also, there is a helper function named getType which returns the type property of an action that wrapped in given action creator.

https://github.com/thebrodmann/deox/blob/3fbdfd25500922410ceeff8d7eff95d1b6e5a780/src/get-type.ts#L1-L13

According to the above helpers, your example can be re-write in the following way to hide all that type verbosity:

import { createActionCreator, getType, ActionType } from 'deox'

export const createUserRequested = createActionCreator(
  'CREATE_USER_REQUESTED',
  resolve => (user: User) => resolve(user),
);

export function* userEditSaga(action: ActionType<typeof createUserRequested>) {
  // ...
}

export const userSagas = [
  takeLatest(getType(createUserRequested), userEditSaga)
]
the-dr-lazy commented 5 years ago

Tomorrow, I will try to make a codesandbox example of usage with redux-saga like the example of usage with redux-thunk.

the-dr-lazy commented 5 years ago

The example can be found in codesandbox. I will close the issue for housekeeping purposes. Feel free to re-open it if needed.

npeham commented 5 years ago

Thank you very much this is awesome :tada:

Please keep up the great work! 👍

SantoJambit commented 4 years ago

I've been using ReturnType without issues so far. Is the only benefit of using ActionType over ReturnType that it returns never if the passed in type is not an action creator? Or am I missing some big benefit here?

the-dr-lazy commented 4 years ago

Or am I missing some big benefit here?

It makes type definition to feel consistent. Every time I want to extract the TypeScript definition for an action I use ActionType without thinking about the input is a reducer or an action creator.

SantoJambit commented 4 years ago

Yeah, I guess it makes it more readable. Thanks.