Home > Software design >  Why do action creators have a type property?
Why do action creators have a type property?

Time:12-09

I'm making a function that accepts an array of Actions, a type that only requires it to have a type property. While writing tests for that function I made a mistake and passed the Action Creator directly instead of the result of calling it, making the test to fail. After I found the error, I realized typescript didn't complain, because BaseActionCreator also has a type property so for typescript they are interchangeable if I pass any of them as an argument (which it seems I don't want to happen).

Is there any particular reason for this? and, is there a way to prevent Action Creators from being accepted as arguments?

An example of what's going can be seen at: https://codesandbox.io/embed/typescript-playground-export-forked-0qjt4k?fontsize=14&hidenavigation=1&theme=dark

import { Action, createAction } from "@reduxjs/toolkit";

function myFunction(actions: Action[]) {
  actions.forEach((action) => console.log(action.type));
}

const customActionCreator = createAction("CUSTOM");

myFunction([customActionCreator()]);
myFunction([customActionCreator]);

CodePudding user response:

A BaseActionCreator (or in your case, a ActionCreatorWithoutPayload) requires the type property to compare itself with other action creators.

We can see it being used inside the .match method.

actionCreator.match = (action: Action<unknown>): action is PayloadAction =>
    action.type === type

This means that any ActionCreatorWithoutPayload is also assignable to Action since they both have the payload and type properties. But we essentially want to disallow ActionCreatorWithoutPayload from being passed to the function.

We could look at what properties ActionCreatorWithoutPayload has addtionally. Since it is a function, it has the call property which we can "blacklist" by intersecting Action with { call?: never }.

function myFunction(actions: (Action & { call?: never })[]) {
  actions.forEach((action) => console.log(action.type));
}

Which gives us the desired error.

myFunction([customActionCreator()]);

myFunction([customActionCreator]);
//          ^^^^^^^^^^^^^^^^^^^ Type 'ActionCreatorWithoutPayload<"CUSTOM">' is 
//                              not assignable to type '{ call?: undefined; }

Playground

  • Related