The Action-Reducer Mapping feature is encountering a type error when handling multiple types of actions

Earlier today, I posed a question about creating a mapping between redux action types and reducers to handle each type explicitly.

After receiving helpful guidance on how to create the mapping, I encountered an error when attempting to use it in creating a reducer.

Consider the following code snippet (it's lengthy, but intended as a minimal example):

export type IActionReducerMapping<S, A extends IReduxAction<string>> = {
    [K in A['type']]: IReducer<S, Extract<A, { type: K }>>
};

interface IUser {
    id: number;
    name: string;
    age: number;
}

interface IUserState {
    [id: number]: IUser;
}

interface IAddUserAction {
    type: 'ADD_USER';
    payload: IUser;
}

interface ISetUserNameAction {
    type: 'SET_USER_NAME';
    payload: {
        id: IUser['id'];
        name: IUser['name'];
    }
}

type UserAction = IAddUserAction | ISetUserNameAction;

const mapping: IActionReducerMapping<IUserState, UserAction> = {
    'ADD_USER': (state, action) => ({
        ...state,
        [action.payload.id]: action.payload,
    }),

    'SET_USER_NAME': (state, action) => ({
        ...state,
        [action.payload.id]: {
            ...state[action.payload.id],
            name: action.payload.name,
        }
    }),
};

const userReducer = (state: IUserState, action: UserAction) => mapping[action.type](state, action);

The error received is as follows:

Argument of type 'UserAction' is not assignable to parameter of type 'IAddUserAction & ISetUserNameAction'. Type 'IAddUserAction' is not assignable to type 'IAddUserAction & ISetUserNameAction'. Type 'IAddUserAction' is not assignable to type 'ISetUserNameAction'. Types of property 'type' are incompatible. Type '"ADD_USER"' is not assignable to type '"SET_USER_NAME"'.ts(2345)

I'm struggling to identify where the typing issue lies. Any insights on this would be greatly appreciated.

Playground link for reference.


Edit:

The following modified approach successfully resolves the issue:

const userReducer = (state: IUserState, action: ISetUserNameAction | IAddUserAction) =>  {
    switch (action.type) {
        case 'ADD_USER':
            return mapping[action.type](state, action);
        case 'SET_USER_NAME':
            return mapping[action.type](state, action);
    }
}

It appears that specifying the potential types upfront allows the compiler to understand the compatibility better. The automatic inference of this remains somewhat mysterious though.

Answer №1

To resolve this issue, I implemented a new function that transforms a mapping into a reducer:

export const createReducerFromMapping = <S, A extends IReduxAction<string>>(mapping: IActionReducerMapping<S, A>) =>
    (state: S, action: A): S =>
        mapping[action.type](state, action);

With this in place, the usage is simplified to:

const userReducer = createReducerFromMapping(mapping);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Having difficulty in converting JSON objects into key/value pairs in Angular 7

I have a task to convert my JSON data from its current format as shown below: cacheMapDataDto = [{ "cacheName": "cache_nchl_individual_type", "count": 2, "mapObj": { "NCHL_BI_BATCH_VERIFICATION": false, "NCHL_STL_BATCH_VERIFICATIO ...

Oops! An issue occurred: [RunScriptError]: Running "C:Windowssystem32cmd.exe /d /s /c electron-builder install-app-deps" resulted in an error with exit code 1

query: https://github.com/electron/electron/issues/29273 I am having trouble with the installation package as it keeps failing and showing errors. Any tips or suggestions would be highly appreciated. Thank you! ...

Access to property 'foo' is restricted to an instance of the 'Foo' class and can only be accessed within instances of 'Foo'

In my Typescript code, I encountered an error with the line child._moveDeltaX(delta). The error message reads: ERROR: Property '_moveDeltaX' is protected and only accesible through an instance of class 'Container' INFO: (me ...

Is the state variable not being properly set by using React's setState within the useCallback() hook?

Within a React FunctionComponent, I have code that follows this pattern: const MyComponent: React.FunctionComponent<ISomeInterface> = ({ someArray, someFunction }) => { const [someStateObjVar, setSomeStateObjVar] = React.useState({}); const [ ...

``Implementing a method to save the output of an asynchronous request in a global variable for future manipulation

It's been a week and I still can't figure this out. Being new to front-end development, I'm struggling with storing the response from subscribe in a global variable for future use. ngOnInit(): void { this.http.get<APIResponse>('ur ...

Challenges arise when using axios in conjunction with React and Redux for a nodejs/express application integrated with passport and jwt

Recently, I developed an API with the route api/login and have a client in the server folder. The URL can be accessed without http due to the proxy implementation. However, when I submit a request, I encounter a 500 error in the browser (from the catch(err ...

Conditional application of Angular animations is possible

After implementing the fadein effect from Angular-Animations in my ASP.NET based Angular project, I encountered an issue where only the first row is faded-in while the other rows are not displayed when using *ngIf. Here is a snippet of the code: <ng-te ...

Guide on creating a 4-point perspective transform with HTML5 canvas and three.js

First off, here's a visual representation of my objective: https://i.stack.imgur.com/5Uo1h.png (Credit for the photo: ) The concise question How can I use HTML5 video & canvas to execute a 4-point perspective transform in order to display only ...

Is it possible to pass a prop from a parent container to children without knowing their identities?

I am currently working on a collapsible container component that can be reused for different sections of a form to display fields or a summary. Each container would include a Form object passed as a child. Here is the basic structure of my code: function C ...

Error: ngModel does not reflect dynamic changes in value

After calling a Spring service, I am receiving JSON data which is stored in the "etapaData" variable. 0: id: 266 aplicacao: {id: 192, nome: "Sistema de Cadastro", checked: false} erro: {id: 220, nome: "Falta de orçamento", checked: false} perfil: {id: 8, ...

Error message indicating that the function 'slice' is not defined for the data variable

I've already searched for solutions to a similar issue, but none of them worked for me. I'm dealing with an object that fetches a list of cities including their names, populations, and states. Here is my HTTP service request: cidadesUrl = 'h ...

What are the signs that derived data has outgrown the capabilities of redux selectors?

It is often recommended to keep the state minimal and utilize memoized selectors (such as reselect library) for derived data. While this approach is generally effective, there are scenarios where storing data in the store may be more suitable. In my applic ...

You are unable to apply 'use client' on a layout element in Next.js

While attempting to retrieve the current page from the layout.txt file, I encountered errors after adding 'use client' at the top of the page: Uncaught SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data parseMod ...

Place a request for the Material UI switch on the platform

I'm currently in the process of constructing a switch that gets its checked value from the data retrieved from the backend. When a user toggles it, a PUT request is sent to the backend for updating the choice. Although I've made progress on it, ...

Utilize async/await to send images using node mailer

How can I correctly access mailOptions in the triggerExample.ts file? mail.ts: export const sendNewMail = async (html: string, emails: string[]) => { let smtpTransport = nodemailer.createTransport({ service: "Gmail", auth: { u ...

Tips for Sending Variables in HTTP Requests in Angular 9

'''Is there a way to pass fromDateTime and toDateTime as parameters in this link?''' export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration { const protectedResourceMap = new Map<string, Array& ...

Restrict a class to contain only functions that have a defined signature

Within my application, I have various classes dedicated to generating XML strings. Each of these classes contains specific methods that take input arguments and produce a string output. In order to enforce this structure and prevent the addition of methods ...

Upgrading from Angular 2 to 4 causes compilation failure in project

Recently, I upgraded my Angular project from version 2 to version 4. The steps I followed for this upgrade are as follows: 1- Deleted the /node_modules/ folder 2- Executed the following command: npm install @angular/common@latest @angular/compiler@lat ...

What is the process for retrieving the API configuration for my admin website to incorporate into the Signin Page?

My admin website has a configuration set up that dynamically updates changes made, including the API. However, I want to avoid hardcoding the base URL for flexibility. How can I achieve this? Please see my admin page with the config settings: https://i.st ...

Adjusting the settimeout delay time during its execution

Is there a way to adjust the setTimeout delay time while it is already running? I tried using debounceTime() as an alternative, but I would like to modify the existing delay time instead of creating a new one. In the code snippet provided, the delay is se ...