Search code examples
reactjstypescript

useReducer's initialState is typed as never?


initialState has the error: Argument of type '{ email: string; password: string; valid: boolean; }' is not assignable to parameter of type 'never'.ts(2345)


function reducer(state: IState, action: IFluxAction) {
  const Users = UsersFetch()
  switch (action.type) {
    case 'email':
      const email = action.value
      const valid = Users.find(e => e === email) ? true : false

      return {
        email,
        valid,
      }
    case 'password':
      const password = action.value
      return {
        password,
      }
    default:
      throw new Error()
  }
}
  const initialState = {
    email: '',
    password: '',
    valid: false,
  }
  const [state, dispatch] = React.useReducer(reducer, initialState)

What's the proper way of typing this to satisfy the error?

React 16.8.1 Typescript 3.3.1

Should be (is fixed by) adding ...state to the returns, like

  switch (action.type) {
    case 'email':
      const email = action.value
      const valid = Users.find(e => e === email) ? true : false

      return {
        ...state,
        email,
        valid,
      }
    case 'password':
      const password = action.value
      return {
        ...state,
        password,
      }
    default:
      throw new Error()
  }

Additionally - as suggested by @madflanderz , setting IState as the reducer's expected return value helps catch issues like this.


Solution

  • The issue is most likely with your reducer's declaration. The initialState's type must be the same as the state's type and return value in the reducer function.

    This will work:

    function reducer(state: {email: string}) {
      return state
    }
    const initialState = {
      email: '',
    }
    const [state, dispatch] = React.useReducer(reducer, initialState)
    

    This will produce an error:

    // state has a different type than initialState.
    function reducer(state: {address: string}) {
      // No return statement.
    }
    const initialState = {
      email: '',
    }
    const [state, dispatch] = React.useReducer(reducer, initialState) // Error
    

    In the React's typing you can see that the useReducer generic function always expects the initialState type to be of a ReducerState<R> type. The ReducerState<R> is a conditional type that tries to infer the proper state's type and falls back to never if it fails.