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;