Search code examples
typescriptdispatchuse-reducer

How type the interface as Dispatch<Action>?


I am trying to make an interface that has the dispatch function, but I am not using the Redux.

interface DispatchProps {
  dispatch: (action: { type: string }) => void;
}

export function numberAddTwo({ dispatch }: DispatchProps) {
  dispatch({ type: '@addTwoToNumber' })
}

But when I call the function here

const [state, dispatch] = useReducer(reducer, initialState);
<button className="btn" onClick={() => numberAddTwo(dispatch)}>+2</button>

The error appears at the dispatch parameter

Argument of type 'Dispatch<Action>' is not assignable to parameter of type 'DispatchProps'.
  Property 'dispatch' is missing in type 'Dispatch<Action>' but required in type 'DispatchProps'.ts(2345)

I tried to find the Action interface but I guess that is a Generic type.


Solution

  • You can use the Dispatch type provided by React for the parameter type and then pass a discriminated union type Action which could contain all the actions that will update the reducer state:

    import { Dispatch, useReducer } from "react";
    
    const ADD_TWO_TO_NUMBER = "@addTwoToNumber"
    
    type Action = | { type: typeof ADD_TWO_TO_NUMBER }
    
    function numberAddTwo(dispatch: Dispatch<Action>) {
      dispatch({ type: ADD_TWO_TO_NUMBER })
    }
    
    

    Here's an example of it working together with the app code:

    import { Dispatch, useReducer } from "react";
    
    const ADD_TWO_TO_NUMBER = "@addTwoToNumber"
    
    type Action = | { type: typeof ADD_TWO_TO_NUMBER }
    
    type State = { number: number };
    
    function numberAddTwo(dispatch: Dispatch<Action>) {
      dispatch({ type: ADD_TWO_TO_NUMBER })
    }
    
    const reducer = (state: State, action: Action) => {
      switch (action.type) {
        case ADD_TWO_TO_NUMBER:
          return { ...state, number: state.number + 2 };
        default:
          return state;
      }
    }
    
    export default function App() {
      const [state, dispatch] = useReducer(reducer, { number: 1 })
    
      return (
        <div className="App">
          {state.number}
          <button className="btn" onClick={() => numberAddTwo(dispatch)}>+2</button>
        </div>
      );
    }