Typescript: How to type the Dispatch in Redux
Asked Answered
P

3

27

For example I want to remove the dispatch: any here:

export const fetchAllAssets = () => (dispatch: any) => {
  dispatch(actionGetAllAssets);
  return fetchAll([getPrices(), getAvailableSupply()]).then((responses) =>
    dispatch(actionSetAllAssets(formatAssets(responses))));
}

There are 2 actions I dispatch above, actionsGetAllAssets and actionsSetAllassets.

Here are the interfaces and actionCreators for both:

// Interfaces
interface IActions {
  GET_ALL_ASSETS: string;
  SET_ALL_ASSETS: string;
  GET_MARKET_PRICES: string;
  SET_MARKET_PRICES: string;
  ADD_COIN_PORTFOLIO: string;
  ADD_COINS_PORTFOLIO: string;
  UPDATE_COIN_PORTFOLIO: string;
  REMOVE_COIN_PORTFOLIO: string;
} 

interface IGetAllAssets {
  type: IActions['GET_ALL_ASSETS'];
  loading: boolean;
}

interface ISetAllAssets {
  type: IActions['SET_ALL_ASSETS'];
  assets: IAsset[];
  loading: boolean;
}

// ACTION CREATORS
const actionGetAllAssets = () => ({
  type: Actions.GET_ALL_ASSETS,
  loading: true
});

const actionSetAllAssets = (data: any) => ({
  type: Actions.SET_ALL_ASSETS,
  assets: data,
  loading: false
});

So then I tried the following:

export const fetchAllAssets = () => (dispatch: IGetAllAssets | ISetAllAssets) => {
  console.log('fetchAllAssets', dispatch);
  dispatch(actionGetAllAssets);
  return fetchAll([getPrices(), getAvailableSupply()]).then((responses) =>
    dispatch(actionSetAllAssets(formatAssets(responses))));
}

However it produces this Typescript error:

Cannot invoke an expression whose type lacks a call signature. Type 'IGetAllAssets | ISetAllAssets' has no compatible call signatures.ts(2349)

Thoughts? Or is there a different way to type a dispatch event?

Protease answered 23/2, 2019 at 18:38 Comment(3)
so was it working with when you defined the dispatch as any?Partake
@KaranGarg yup, it works with any, but thats a hackProtease
Instead of (dispatch: IGetAllAssets | ISetAllAssets) can you try Dispatch<IGetAllAssets> | Dispatch<ISetAllAssets>Partake
P
6

I got a bit further!

Dispatch is an event function, so got this to work:

interface IAllAssets {
  type: IActions['GET_ALL_ASSETS'];
  assets?: IAsset[];
  loading: boolean;
}

// ACTIONS
// Fetch assets from Nomics API V1.
export const fetchAllAssets = () => (dispatch: (arg: IAllAssets) => (IAllAssets)) =>
{
   dispatch(actionGetAllAssets());
   return fetchAll([getPrices(), getAvailableSupply()]).then((responses) =>
       dispatch(actionSetAllAssets(formatAssets(responses))));
}

However I'd still like to create a dispatch type, something like:

interface IAllAssetsDispatch {
  dispatch: (arg: IAllAssets) => (IAllAssets)
}

export const fetchAllAssets = () => (dispatch: IAllAssetsDispatch) => {

But this produces the same lacks a call signature error.

GOT IT!

Forgot about type that's what I needed to use instead of interface for functions:

type DispatchAllAssets = (arg: IAllAssets) => (IAllAssets);

type DispatchMarketPrices = (arg: ISetMarket) => (ISetMarket);

type DispatchAddCoin = (arg: ICoinPortfolio) => (ICoinPortfolio);

type DispatchAddCoins = (arg: ICoinsPortfolio) => (ICoinsPortfolio);

// ACTIONS
// Fetch assets from Nomics API V1.
export const fetchAllAssets = () => (dispatch: DispatchAllAssets) => {
  dispatch(actionGetAllAssets());
  return fetchAll([getPrices(), getAvailableSupply()]).then((responses) =>
    dispatch(actionSetAllAssets(formatAssets(responses))));
}

// Fetch USD, USDC & USDT markets to filter out Exchange List.
export const fetchMarketPrices = (asset: string) => (dispatch: DispatchMarketPrices) => {
  dispatch(actionGetMarketPrices);
  return getMarkets().then((res) => {
    if (res && res.marketUSD && res.marketUSDC && res.marketUSDT) {
      const exchangesForAsset = combineExchangeData(asset, res);
      return dispatch(actionSetMarketPrices(exchangesForAsset));
    }
    else {
      return dispatch(actionSetMarketPrices([]));
    }
  });
}
Protease answered 23/2, 2019 at 23:41 Comment(0)
F
21

The Redux typings have a generic Dispatch<A> type you can use, where A is the type of action.

export const fetchAllAssets = () => (dispatch: Dispatch<IGetAllAssets | ISetAllAssets>) => {
  // ...
}
Flemings answered 27/2, 2019 at 20:26 Comment(1)
I used this approach but in conjunction with one re-usable Action type defined as a union type. That way it is just (dispatch: Dispatch<Action>)Rusell
P
6

I got a bit further!

Dispatch is an event function, so got this to work:

interface IAllAssets {
  type: IActions['GET_ALL_ASSETS'];
  assets?: IAsset[];
  loading: boolean;
}

// ACTIONS
// Fetch assets from Nomics API V1.
export const fetchAllAssets = () => (dispatch: (arg: IAllAssets) => (IAllAssets)) =>
{
   dispatch(actionGetAllAssets());
   return fetchAll([getPrices(), getAvailableSupply()]).then((responses) =>
       dispatch(actionSetAllAssets(formatAssets(responses))));
}

However I'd still like to create a dispatch type, something like:

interface IAllAssetsDispatch {
  dispatch: (arg: IAllAssets) => (IAllAssets)
}

export const fetchAllAssets = () => (dispatch: IAllAssetsDispatch) => {

But this produces the same lacks a call signature error.

GOT IT!

Forgot about type that's what I needed to use instead of interface for functions:

type DispatchAllAssets = (arg: IAllAssets) => (IAllAssets);

type DispatchMarketPrices = (arg: ISetMarket) => (ISetMarket);

type DispatchAddCoin = (arg: ICoinPortfolio) => (ICoinPortfolio);

type DispatchAddCoins = (arg: ICoinsPortfolio) => (ICoinsPortfolio);

// ACTIONS
// Fetch assets from Nomics API V1.
export const fetchAllAssets = () => (dispatch: DispatchAllAssets) => {
  dispatch(actionGetAllAssets());
  return fetchAll([getPrices(), getAvailableSupply()]).then((responses) =>
    dispatch(actionSetAllAssets(formatAssets(responses))));
}

// Fetch USD, USDC & USDT markets to filter out Exchange List.
export const fetchMarketPrices = (asset: string) => (dispatch: DispatchMarketPrices) => {
  dispatch(actionGetMarketPrices);
  return getMarkets().then((res) => {
    if (res && res.marketUSD && res.marketUSDC && res.marketUSDT) {
      const exchangesForAsset = combineExchangeData(asset, res);
      return dispatch(actionSetMarketPrices(exchangesForAsset));
    }
    else {
      return dispatch(actionSetMarketPrices([]));
    }
  });
}
Protease answered 23/2, 2019 at 23:41 Comment(0)
S
0

Action is an interface with type: T (usually a string), that accepts a generic:

export interface Action<T = any> {
  type: T
}

here is an example:

type GetBannerList = GetAllBanner | Error | Loading;

type Loading = {
  type: string;
};

type GetAllBanner = {
  type: string;
  payload: PaginatedResponse<Banner>;
};

type Error = {
  type: string;
  payload: Error;
};

export const getBannersList = (
  typeRegistration?: BannerRegistrationType
) => async (dispatch: Dispatch<GetBannerList>) => {

  try {
    dispatch(increaseLoading());

    const payload = await BannerApi.getBannersList(typeRegistration);
    dispatch({
      type: GET_ALL_BANNERS,
      payload,
    });
  } catch (err) {
    if (err instanceof Error) {
      dispatch({
        type: PARTNERSHIP_ERROR,
        payload: err,
      });
    }
  } finally {
    dispatch(decreaseLoading());
  }
};
Saloma answered 18/1 at 13:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.