5
votes

I have the following actions:

export const ActionTypes = {
  CREATE_OH:                  type('[ORDERHEAD] Create Orderhead'),
  MODIFY_SELECTED_OH:    type('[ORDERHEAD] Select Orderhead'),     
};

export class CreateOHAction implements Action {
  type = ActionTypes.CREATE_OH

  constructor(public payload: OrderHead[]) { }
}

export type Actions
  =   CreateOHAction
  |   SelectOHAction;

With the following base reducer setup

export interface State {
  orderids: string[];
  entities: { [orderID: string]: OrderHead };
  selectedOhID: string | null;
};

// Set initial state to empty
const initialState: State = {
  orderids : [],
  entities: {},
  selectedOhID: null,

};
export function OHreducer(state = initialState, action: orderhead_imp.Actions):  State{
      switch(action.type){

        case orderhead_imp.ActionTypes.CREATE_OH: 
            let orders = action.payload;
            let newOrders = orders.filter(orderhead => !state.entities[orderhead.orderID]);

            let newOrderIds = newOrders.map(orderhead => orderhead.orderID);
            let newOrderHeadEntities = newOrders.reduce((entities: { [orderID: string]: OrderHead }, orderhead: OrderHead) => {
              return Object.assign(entities, {
                [orderhead.orderID]: orderhead
              });
            }, {});

            return {
              orderids: [ ...state.orderids, ...newOrderIds ],
              entities: Object.assign({}, state.entities, newOrderHeadEntities),
              selectedOhID: state.selectedOhID
              };

        default:
            return state;

    };
}

This works fine, however, if I introduce another action:

export class SelectOHAction implements Action {
  type = ActionTypes.MODIFY_SELECTED_OH 

  constructor(public payload: string) { }
}

Notice, the payload for this action only is string, as soon as this saved, or tried to compile, typescript now states: "filter does not exisit on type string|OrderHead[]"

Now if I go into my reducer, and add a new case:

case orderhead_imp.ActionTypes.MODIFY_SELECTED_OH: {
 return {
  orderids:  state.orderids,
  entities: state.entities,
  selectedOhID: action.payload
};
}

I get the typescript errors when mapping action.payload of:

Throws the TS error "string|OrderHead[] is not assignable to type string"

Obviously in both cases the payload has a different data structure, do I need to change my code any other way to ensure each case is picking up the correct type for action.payload?

Update

So if in my actions I define the payload as "any" instead of "string" it seems to compile and work without issue, however this seems very hacky (and not the expected behaviour)

export class SelectOHAction implements Action {
  type = ActionTypes.MODIFY_SELECTED_OH 

  constructor(public payload: any) { }
}
1
In your state try selectedOhID: string|null to just selectedOhID: string - Victor Godoy
No, I tried this first of all, but if I set type in my reducer of any for selectedOhID it still fails, its a type checking error from the payload value defined in actions, not an error from the reducer type definitions. - crooksey
What version of typescript are you using? There was a breaking change (arguably a bug fix) in typescript 2.1+ that affects string literals (and therefore discriminated unions). - rob3c
2.1.5, is this a known issue or is there now a fix? - crooksey
Any update about this issue? I am using ngrx store and effects 6.0.1 - xzegga

1 Answers

0
votes

This is an issue with Typescript >2.1 and the type util of ngrx.

With typescript 2.1 and above now you can simply define actions as

export const CREATE_OH: '[ORDERHEAD] Create Orderhead';
export class CreateOHAction implements Action {
   type = CREATE_OH

   constructor(public payload: OrderHead[]) { }
}

Now everywhere you used item.ActionTypes.CREATE_OH, replace it with item.CREATE_OH. The types will flow as expected with typescript 2.1