4
votes

I'm experiencing a typescript error on my container component when trying to create my mapDispatchToProps function because my Thunk function doesn't return an object with property 'Type' in it. My Thunk returns a Promise, which itself doesn't have the 'Type' property, but does dispatch an action that does have 'Type' in it. I'm not sure how to tell typescript that this is ok.

The error I get is

Argument of type '(dispatch: Dispatch<ActionTypes>, getState: () => IStoreState) => Promise<void>' is not assignable to parameter of type 'ActionTypes'.
Property 'type' is missing in type '(dispatch: Dispatch<ActionTypes>, getState: () => IStoreState) => Promise<void>'.

Action Types:

export interface IFetchJokeSuccessAction {
  readonly type: ActionTypeKeys.FETCH_JOKE_SUCCESS;
  readonly payload: string;
}

export interface IFetchJokeInProgressAction {
  payload: string;
  readonly type: ActionTypeKeys.FETCH_JOKE_INPROGRESS
}

export interface IFetchJokeFailAction {
  readonly type: ActionTypeKeys.FETCH_JOKE_FAIL;
  readonly payload: string;
}

export interface IClearJokeAction {
  readonly type: ActionTypeKeys.CLEAR_JOKE
}

type ActionTypes = IFetchJokeSuccessAction | IFetchJokeInProgressAction | IFetchJokeFailAction | IClearJokeAction;

Here's my dispatch on my component:

interface IDispatchProps {
  clearJoke: () => any;
  fetchJoke: () => any;
}
const mapDispatchToProps = (dispatch: Dispatch<ActionTypes>): IDispatchProps => {
  return {
    clearJoke: () => dispatch(clearJoke()), // No problem, this is a regular action
    fetchJoke: () => dispatch(fetchJoke()) // Problem, this is a Thunk
  }
};

Here are my actions:

import { Dispatch } from 'redux';
import { fetchJokeAPI } from '../api/jokeApi';
import IStoreState from '../store/IStoreState';
import { ActionTypeKeys as keys, ActionTypes, IClearJokeAction, IFetchJokeFailAction, IFetchJokeInProgressAction, IFetchJokeSuccessAction} from './ActionTypes';

export function fetchJoke(): (dispatch: Dispatch<ActionTypes>, getState: () => IStoreState) => Promise<void>  {
  return async (dispatch: Dispatch<IFetchJokeInProgressAction | IFetchJokeSuccessAction | IFetchJokeFailAction>, getState: () => IStoreState) => {
    dispatch(fetchJokeInProgress())
    try {
      const jokePayload = await fetchJokeAPI();

      dispatch(fetchJokeSuccess(jokePayload));
    } catch (err) {
      dispatch(fetchJokeFail(err));
    }
  }
}

export function fetchJokeSuccess(payload: string): IFetchJokeSuccessAction {
  return {
    payload,
    type: keys.FETCH_JOKE_SUCCESS,
  }
}

export function fetchJokeInProgress(): IFetchJokeInProgressAction {
  return {
    payload: 'Fetching a good joke.',
    type: keys.FETCH_JOKE_INPROGRESS
  }
}

export function fetchJokeFail(error: Error): IFetchJokeFailAction {
  return {
    payload: JSON.stringify(error),
    type: keys.FETCH_JOKE_FAIL
  }
}
1
late, but: now you can use the ThunkDispatch instead of the Dispatch type, covers both cases. (since redux-thunk 2.3)gorhawk

1 Answers

0
votes
export function fetchJoke(): (dispatch: Dispatch<ActionTypes>, getState: () => IStoreState) => Promise<void>  {
  return async (dispatch: Dispatch<IFetchJokeInProgressAction | IFetchJokeSuccessAction | IFetchJokeFailAction>, getState: () => IStoreState) => {
    dispatch(fetchJokeInProgress())
    try {
      const jokePayload = await fetchJokeAPI();

      dispatch(fetchJokeSuccess(jokePayload));
    } catch (err) {
      dispatch(fetchJokeFail(err));
    }
  }
}

Since this will be a thunk action creator you have to specify what type of action creator fetchJoke() is. you are using thunk action creator, but there is not any thunk type in your code.

You are saying that you are returnin Promise<void> but you have no return used in the function.

import { ThunkAction } from "redux-thunk";
import { Dispatch, ActionCreator } from "redux";

export const fetchJoke():ActionCreator<
ThunkAction<
  Promise<IFetchJokeSuccessAction|IFetchJokeFailAction>, //return value
  StoreState, // your app's store
  null, // this is extraArgument is passed to the thunk to fetch data
  IFetchJokeSuccessAction|IFetchJokeFailAction // which actions you are using in this thunk func
>
> =()=>  {
  return async (dispatch: Dispatch<ActionTypes>, getState: () => IStoreState) => {
    dispatch(fetchJokeInProgress())
    try {
      const jokePayload = await fetchJokeAPI(); 
      if(jokePayload){
        //  jokePayload could be undefined. setting type guard
        return dispatch(fetchJokeSuccess(jokePayload));
      }
    } catch (err) {
      return  dispatch(fetchJokeFail(err));
    }
  }
}

type ActionTypes = IFetchJokeSuccessAction | IFetchJokeInProgressAction | IFetchJokeFailAction | IClearJokeAction;