Search code examples
typescriptionic-frameworkreduxreact-reduxdispatch

Cannot get access to properties on payload in the reducers when dispatching actions using react-redux and typescript


In my reducers I want to access the payload property to update the store, but Typescript seems to complain about the type of the property that I want to access.

//_constants.ts

export const resultsConstants = {
  STORE_RESULT: "STORE_RESULT",
  DELETE_RESULT: "DELETE_RESULT",
};

//_actiontypes.ts

import { resultsConstants } from "../../_constants";

export interface StoreResult {
  type: typeof resultsConstants.STORE_RESULT;
  payload: { value: number };
}

export interface DeleteResult {
  type: typeof resultsConstants.DELETE_RESULT;
  payload: {
    id: string;
  };
}

// results.actions.ts dispatching

export const storeResult = (val: number) => {
  return (dispatch: Dispatch<StoreResult>) => {
    dispatch({
      type: resultsConstants.STORE_RESULT,
      payload: { value: val },
    });
  };
};

export const deleteResult = (id: string) => {
  return (dispatch: Dispatch<DeleteResult>) => {
    dispatch({ type: resultsConstants.DELETE_RESULT, payload: { id: id } });
  };
};

export type ResultActionTypes = StoreResult | DeleteResult;

// reducer.ts

const initialState: StoredResults = {
  results: [],
};

export const results = (
  state = initialState,
  action: ResultActionTypes
): StoredResults => {
  switch (action.type) {
    case resultsConstants.DELETE_RESULT:
      return state;
    case resultsConstants.STORE_RESULT:
      const oldState = { ...state };
      oldState.results = state.results.concat(action.payload.value); /* cannot access this value here */
      return { ...state };
    default:
      return state;
  }
};

This is the error I am getting from TS:

Property 'value' does not exist on type '{ value: number; } | { id: string; }'.
  Property 'value' does not exist on type '{ id: string; }'.ts(2339)

Even though I combined the actiontypes in the results.actions.ts files it complains that the value property does not exist on the action type that is passed by the dispatching function.

Any help or advice on how to do it differently would be greatly appreciated!

Thanks!

EDIT

By further splitting the action type interfaces into a payload interface, and typecasting the payload in the reducer, I got this to work:

// results.types.ts

export interface StoreResultPayload {
  value: number;
}

export interface StoreResultType {
  type: typeof resultsConstants.STORE_RESULT;
  payload: StoreResultPayload;
}

// results.reducer.ts

const val = action.payload as StoreResultPayload;
oldState.results = state.results.concat(val.value);

This still feels like a workaround for me. Shouldn't typescript be able to infer the payload anyway due to the action types being combined with the union operator?


Solution

  • Typecasting the payload object (to tell Typescript: "Trust me, I know what I am doing") is the only workaround I could find for now. See the edited version of the question for the answer.