Typescript types of PayloadAction

Viewed 1636

I am using createSlice like this:

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

const initialState = {} as Order;

const order = createSlice({
  name: 'order',
  initialState,
  reducers: {
    setOrder: (_state, { payload }: PayloadAction<Order | null>) => payload,
  },
});

I get the following error:

Type 'SalesOrder | null' is not assignable to type 'void | SalesOrder | WritableDraft<SalesOrder>'.
  Type 'null' is not assignable to type 'void | SalesOrder | WritableDraft<SalesOrder>'.ts(2322)

When I hover over the function I get the following types:

function(_state: WritableDraft<SalesOrder>, { payload }: PayloadAction<SalesOrder | null>): SalesOrder | null

How do I fix this? I want the value to be either Order or null.

2 Answers

I can't test this right now, but you error might come from the line :

const initialState = {} as Order;

You're basically telling typescript that the initialState variable Has to be an Order (and lying to the compiler by passing an empty object), and then in your reducer you want to make it either Order or null, to which typescript will tell you it can't be null or Order, it has to be an order.

You should try:

const initialState: null | Order = null

And tell me about it

EDIT: I have found the solution in the redux toolkit docs, and it's not intuitive !

First step :

type OrderState = Order | null;

So far so good. We have defined the type that your state should have.

Now, for the messy part, when you actually create your initial state constant, typescript will narrow its type to its value, whatever type you tell it it is.

So when you write this :

const initialState: OrderState = null

Typescript will understand "this constant is of type null", so your create slice function will understand "the state is of type null".

The workaround, is to cast the correct type onto the null value :

const initialState = null as OrderState

I know that both initialState declarations should produce the same type inferring, but it apparently doesn't work that way.

Doing so, this line here :

setOrder: (_state, { payload }: PayloadAction<OrderState>) => payload,

will not output an error, because every part of you code knows what type is which.

And by the way, assigning an empty object litteral to the initialState variable is not a very good idea, because your state will never be of type {}. It's either an order meaning there is something, or null meaning there is no order.

I highly recommend reading the "usage with typescript" part of the redux toolkit docs as I don't think this will be your last typescript/redux weird bug. I've had tons !

I think you should actually set the values of the initial state instead of asserting an empty object to be of type Order. Here I just added in random properties as you did not include the type Order(just change it to the actual values). In addition to that I set the initialState to either be of type Order or of null.

type InitialOrderState = { Order | null };

const initialState: InitialOrderState = {
    orderId: "",
    status: "",
};

const order = createSlice({
  name: 'order',
  initialState,
  reducers: {
    setOrder: (state, { payload }: PayloadAction<InitialOrderState>) => payload,
  },
});
Related