ThunkAction getting returned by dispatch instead of the Return Type of the ThunkAction

Viewed 645

How can I configure Typescript to work with react-redux and redux-thunk so that dispatch returns the return type of the ThunkAction getting dispatched instead of a ThunkAction<a,b,c,d,e>?

export function addPerson(
  person: Person
): PersonActionTypes {
  return {
    type: ADD_PERSON,
    person,
  }
}


export const savePerson = (
  person: Person
): ThunkAction<
  Promise<Person>,
  RootState,
  unknown,
  Action<string>
> => async (dispatch): Promise<Person> => {
  const savedPerson = await PersonApi.save(person)
  dispatch(addPerson(savedPerson))
  return savedPerson
}

When I dispatch this action, I would like for it return a Promise<Person> but dispatch returns ThunkAction<Promise<void>, RootState, unknown, Action<string>>

const savedPerson = await dispatch(savePerson(person))
// savedPerson is a ThunkAction as far as TypeScript is concerned but I would like it to be a Person.
2 Answers

Faced the same problem. After some time found a solution which worked for me. The problem here is that dispatch by default gets type Dispatch and it is ok for simple actions, but is not for ThunkAction.

My thunk action is described like this:

type AppThunk<ReturnType = void> = ThunkAction<ReturnType, StateType, unknown, Action<string>>;
export const thunkAction = (id: number): AppThunk<Promise<{ status: string }>> => async (dispatch) => { ...do something, return correct type };

I've created type for my thunk-dispatch

export type AppThunkDispatch = ThunkDispatch<StateType, unknown, Action<string>>;

And after that I was able to use the return value without type errors, like this:

const data  = await (dispatch as AppThunkDispatch)(thunkAction(33));
if (data.status === 'success') { console.log('perfect') }

If you have only thunk actions to dispatch in this particular component, think you can define dispatch type once

const dispatch: AppThunkDispatch = useDispatch();

and avoid (dispatch as AppThunkDispatch) casting

Here is my custom type:

export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, Action<string>>
export function createThunk<T>(thunk: AppThunk<T>): T { return thunk as any };

Then, use it like the following:

export const getElementRefById = (elementId: string) => createThunk(async (dispatch, getState): Promise<ElementRef> => {
...actions
return data;
}

The return type for the above method will be the Promise<ElementRef> type.

Related