Search code examples
angularreduxngrx-store

@ngrx/store update from v10 to v11 / v12


Good day.

I need help with the following issue. A while back we updated our project to use Angular 12 and I have been trying to update @ngrx/store from v10 to v11 (or v12). I did try this update when we were still using Angular 11 as well, but I keep getting this error:

Argument of type 'ReducerTypes<unknown, [ActionCreator<"EnableForm", () => TypedAction<"EnableForm">>]>' is not assignable to parameter of type 'ReducerTypes<AppState, ActionCreator<string, Creator<any[], object>>[]>'.
  Types of property 'reducer' are incompatible.
    Type 'OnReducer<unknown, [ActionCreator<"EnableForm", () => TypedAction<"EnableForm">>]>' is not assignable to type 'OnReducer<AppState, ActionCreator<string, Creator<any[], object>>[]>'.
      Type 'unknown' is not assignable to type 'AppState'.

My reducer looks something like this:

import { Action, createReducer, on } from '@ngrx/store';
import * as MyAction from '../actions';
import { AppState } from '../state';
import { onEnableForm, onDisableForm } from './form.reducer';

const initialState: AppState = {
    form: {
         disabled: false,
         ...
    },
    ...
};
export function storeReducer(state: AppState = initialState, action: Action): AppState {
    return createReducer<AppState>(
        state,
        on(MyAction.InitializeState, MyAction.ResetState, () => initialState),
        on(MyAction.DestroyState, () => undefined),
        onEnableForm,
        onDisableForm
    )(state, action);
}

actions.ts looks like this:

import { createAction } from '@ngrx/store';

export const InitializeState = createAction('[MyFeatureStore] InitializeState');

export const ResetState = createAction('[MyFeatureStore] ResetState');

export const DestroyState = createAction('[MyFeatureStore] DestroyState');

export const EnableForm = createAction('[MyFeatureStore] EnableForm');

export const DisableForm = createAction('[MyFeatureStore] DisableForm');

And form.reducer.ts looks like this:

import { on } from '@ngrx/store';
import * as MyAction from '../actions';
import { AppState } from '../state';

export const onEnableForm = on(MyAction.EnableForm, (state: AppState) => {
    return {
        ...state,
        form: {
            ...state.form,
            disabled: false
        }
    };
});
export const onDisableForm = on(MyAction.DisableForm, (state: AppState) => {
    return {
        ...state,
        form: {
            ...state.form,
            disabled: true
        }
    };
});

This works okay in v10, but I can't figure out why it's not working in v11.


Solution

  • The thing that is happening is that angular and all of the typescript world are moving to more and more strongly typed rules, so when you are creating the on function outside the pipe, typescript cant infer what's going on.

    The thing that you can do is to extend the types of your on functions like so

    export const onEnableForm = on<AppState, any>(EnableForm, (state: AppState): AppState => {
      return {
        ...state,
        form: {
          ...state.form,
          disabled: false,
        }
      };
    });
    export const onDisableForm = on<AppState, any>(
      DisableForm,
      (state: AppState): AppState => {
        return {
          ...state,
          form: {
            ...state.form,
            disabled: true
          }
        };
      }
    );
    

    You can use it as a workaround until you find the appropriate types to use instead of the any. Here is a stackblitz with the proposed solution.