Cerulean
Cerulean

Reputation: 6013

How to use Redux Tool Kit 'mutations' with TypeScript: "Property 'push' does not exist on type"

WebStorm debug image

I'm working with the Redux Tool Kit in a TypeScript project. One of the things the Tool Kit allows is using mutations inside reducers, although one is not directly mutating the state -- when one is using their createReducer call:

const todosReducer = createReducer([], {
  ADD_TODO: (state, action) => {
    // "mutate" the array by calling push()
    state.push(action.payload)
  },
  TOGGLE_TODO: (state, action) => {
    const todo = state[action.payload.index]
    // "mutate" the object by overwriting a field
    todo.completed = !todo.completed
  },
  REMOVE_TODO: (state, action) => {
    // Can still return an immutably-updated value if we want to
    return state.filter((todo, i) => i !== action.payload.index)
  }
})

I've defined my state like this:

type InitialArticlesState = {
    articles: Articles[];
    currentArticle: Article | undefined;
    tags: TagList;
    currentTags: TagList;
    currentArticleNumber: number;
    totalArticlesNumber: number;
    currentSearchString: string;
    error: string;
};

const initialState: InitialArticlesState = {
    articles: [],
    currentArticle: undefined,
    tags: [],
    currentTags: [],
    currentArticleNumber: 0,
    totalArticlesNumber: 0,
    currentSearchString: '',
    error: ''
};

TypeScript now complains about state.push here:

[articlesReceived.type]: (state, action: PayloadAction<Articles[]>) => {
        state.push(action.payload);
    },

saying

enter image description here

What is the best way to solve this? I'm a relative TS newbie, so it's not clear to me how to start.

Edit

It would seem that when the app arrives at the reducer, state is a proxy for something else. So I'm not sure that types make sense in this context -- what is defined in the code is not what arrives at runtime...or is there a way to do it?

Upvotes: 1

Views: 1357

Answers (1)

markerikson
markerikson

Reputation: 67499

It looks like there's a few different issues here.

First, your reducer function does not match the state type you've defined code-wise. Your initialState value is an object, not an array. But, your code is acting like state is an array by calling state.push(), which is why TypeScript is giving you the error telling you that's not possible.

Second, your payload is typed as an Article[], but you're calling .push() somewhere. Did you actually want to assign that array into the state and overwrite any existing articles? If so, what you actually want is:

state.articles = action.payload;

Third, by calling createReducer and passing in an object of reducers, you're going to have a harder time trying to type the state and action correctly. Per our Usage with TypeScript docs page, you should use the "builder callback" form of createReducer to get better typing behavior:

createReducer(initialState, builder => {
    builder.addCase(typedActionCreator, (state, action) => {
        // state and action are both correctly inferred here
    });

})

And finally, most of the time you shouldn't be calling createReducer by hand anyway. We recommend using createSlice for most of your logic, which will auto-generate the action creators for you.

Upvotes: 1

Related Questions