Home > database >  How do you define the initial state type in redux toolkit createSlice (And the types are different)
How do you define the initial state type in redux toolkit createSlice (And the types are different)

Time:04-24

It is well documented here:

Defining the Initial State Type

However the problem arises when the initial state types could be of multiple different types.

import { createSlice } from "@reduxjs/toolkit";

// Type definitions
export const UNINITIALIZED = "UNINITIALIZED";
interface ICurrentCustomer {
  id: string;
  name: string;
}
interface ISetCurrentCustomerAction {
  type: string;
  payload: ICurrentCustomer;
}

// Initial state (This is intentional, null has special meaning here in my use case)
// This is the important part right here!!!!!!!!!!!!!!!!!!!!!!!
const initialState: ICurrentCustomer | typeof UNINITIALIZED | null = UNINITIALIZED;

const currentCustomerSlice = createSlice({
  name: "currentCustomer",
  initialState,
  reducers: {
    // I put any on state type here coz it will error
    setCurrentCustomer: (state: any, action: ISetCurrentCustomerAction) => {
      const { id, name } = action.payload;

      state.id = id;
      state.name = name;

      return state;
    },
  },
});

export const { setCurrentCustomer } = currentCustomerSlice.actions;

export default currentCustomerSlice.reducer;

The issue here is redux toolkit will set the type of the state in the store as string. Because obviously UNINITIALIZED is a string. This will produce an error and the console message is:

TypeError: Cannot create property 'id' on string 'UNINITIALIZED'

It is about type inference as mentioned here:

How to describe the shape of the state in createSlice

How could I make my code above work? Any help is appreciated.

Thanks in advance.

CodePudding user response:

You can use type cast with as to have createSlice infer the type correctly like this:

import { createSlice } from "@reduxjs/toolkit";

// Type definitions
export const UNINITIALIZED = "UNINITIALIZED";
interface ICurrentCustomer {
  id: string;
  name: string;
}
interface ISetCurrentCustomerAction {
  type: string;
  payload: ICurrentCustomer;
}

// Define a type for your state
type State = ICurrentCustomer | typeof UNINITIALIZED | null;

// Define your initial state
const initialState: State  = UNINITIALIZED;

const currentCustomerSlice = createSlice({
  name: "currentCustomer",
  // Do a type cast here, this will allow `createSlice` to infer the correct type
  initialState: initialState as State,
  reducers: {
    // You can use State type here now
    setCurrentCustomer: (state: State, action: ISetCurrentCustomerAction) => {
      const { id, name } = action.payload;

      // You will still need to check if state in `null` and `UNINITIALIZED` before accessing it
      if(state!== null && state !== UNINITIALIZED) {
        state.id = id;
        state.name = name;
      }

      // Or do something like this if you want to disregard previous state
      //state = {id, name};

      return state;
    },
  },
});

export const { setCurrentCustomer } = currentCustomerSlice.actions;

export default currentCustomerSlice.reducer;
  • Related