Redux-toolkit: Type for generic action (with or without payload).

Created on 18 Feb 2020  路  5Comments  路  Source: reduxjs/redux-toolkit

Hello,
I'm trying to create a generic reducer with createSlice, I cannot figure out the type to use for the action parameter to allow actions with or without a payload

import { createSlice } from '@reduxjs/toolkit'

interface State {
  loading: boolean
  item?: any
  errors?: any
  payload?: any
}

const initialState: State = {
  loading: false,
}

export const slice = createSlice({
  name: 'myslice',
  initialState,
  reducers: {
    // How to type action for allowing readRequest() or readRequest(somePayload)
    readRequest: (state: State, action: any) => {
      state.loading = true
      if ('payload' in action) {
        state.payload = action.payload
      }
    },
    readSuccess: (state: State, action: any) => {
      state.loading = false
      if ('payload' in action) {
        state.payload = action.payload
      }
    },
  },
})

const { actions } = slice
// error readSuccess is expecting 1 argument
actions.readSuccess()

In this example readSuccess() is expecting 1 argument event if its type is
ActionCreatorWithNonInferrablePayload<string> | ActionCreatorWithoutPayload<string>

Link to codesandbox

Most helpful comment

readRequest(state, action: PayloadAction<string | undefined>){}

is what you're looking for.

All 5 comments

Hmm. Does something like:

readRequest(state, action: PayloadAction<string | void>) {}

work for you?

readRequest(state, action: PayloadAction<string | undefined>){}

is what you're looking for.

With

readRequest(state, action: PayloadAction<string | void>) {}

readRequest is expecting 0 arguments (cannot pass a string)

with

readRequest(state, action: PayloadAction<string | undefined>){}

readRequest can accept a string or nothing but what I'm looking is PayloadAction<any>.
But with any the payload is mandatory.

What seems to work is to use

type anything =
  | number
  | string
  | boolean
  | bigint
  | symbol
  | null
  | object
  | undefined;
readRequest(state, action: PayloadAction<anything>){}

It does not seems right since anything is basically any.

Yeah, that's essentially a design decision. We had to decide if PayloadAction<any> results in either a payload action creator that requires one argument that could be anything, or requires one optional argument.

Since we can't support both, the first one seemed the more common use case - so for your use case you'll have to use that workaround you have there.

My question here is: are you sure you want to write something like that at all? That sounds like you're giving up all type-safety down the road, at which point the question would be why you're using TS at all.
Maybe something going in the direction of wrapping createSlice with a generic might help you keep type-safety.

Thanks @phryneas for the answer, I understand the design decision.

Indeed it looks like a TS anti-pattern, I was trying to understand the type decisions around actions, in the end I think I'll use a proper type for action payloads.

Was this page helpful?
0 / 5 - 0 ratings