Search code examples
typescriptreduxredux-toolkit

How to fix typescript error in redux-toolkit?


I'm doing a redux-toolkit tutorial with typescript. But I'm a typescript beginner.

I don't know what the problem is here. Please give me your insight.

This is an error message. : TS2322: Type 'number' is not assignable to type 'void | State | WritableDraft'.

import {CaseReducer, createSlice, PayloadAction} from "@reduxjs/toolkit";

type State = {
  value: number
}
const increment: CaseReducer<State,PayloadAction<number>> = (state, action) => state.value + action.payload; // error line

export const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0
  },
  reducers: {
    increment,
    decrement: state => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})

export const {increment, decrement, incrementByAmount} = counterSlice.actions;

export default counterSlice.reducer;

Solution

  • An arrow function without curly braces around it is an implied return. So you are returning state.value + action.payload which is a number.

    Redux Toolkit allows you to either return a new state (type State | WritableDraft<State> at the time of this answer, or type State | Draft<State> in newer versions of RTK) or modify the draft state and not return anything (type void). You get a Typescript error because returning number is neither or these.

    You likely want to modify the draft state, so you need curly braces around your function body so that you aren't returning anything.


    These three functions are all valid. Ordered from least to most verbose:

    1. You can increment the value directly using the addition assignment operator +=
    const increment: CaseReducer<State,PayloadAction<number>> = (state, action) => {
      state.value += action.payload;
    }
    
    1. You can assign a new value to the state.value property with assignment operator =
    const increment: CaseReducer<State,PayloadAction<number>> = (state, action) => {
      state.value = state.value + action.payload;
    }
    
    1. (Not recommended) You can return an entirely new state. I am using parentheses around curly braces to return an object with the property value.
    const increment: CaseReducer<State,PayloadAction<number>> = (state, action) => ({
      value: state.value + action.payload
    });
    

    If there were properties other than value you would need to copy them like {...state, value: newValue } which is what you see in traditional Redux reducers. Redux Toolkit makes options 1 and 2 available so that you don't have to do this. But if you chose to return a new state then it must be a complete state.