Search code examples
angularrxjsangular7ngrxreducers

Reducer, entity adapter: Property 'accounts' does not exist on type - Angular 7, Rxjs 6, Ngrx


I get the error:

ERROR in src/app/account/account.reducers.ts(24,37): error TS2339: Property 'accounts' does not exist on type '{ account: Account; } | { accounts: Account[]; }'.
  Property 'accounts' does not exist on type '{ account: Account; }'.

This refers to the line addAll on the adapter in the reducer:

export interface AccountState extends EntityState<Account> {
  allAccountsLoaded : boolean;
}

export const adapter : EntityAdapter<Account> = createEntityAdapter<Account>();

export const initialAccountState: AccountState = adapter.getInitialState({
  allAccountsLoaded: false
});

export function accountReducer(state = initialAccountState, action: AccountActions): AccountState {
  switch(action.type) {
    case AccountActionTypes.AccountLoaded:
      adapter.addOne(action.payload.account, state);

    case AccountActionTypes.AllAccountsLoaded:
      adapter.addAll(action.payload.accounts, {...state, allAccountsLoaded: true});
    default: {
      return state;
    }
  }
}

but when I look at the relevant action of the reducer, they payload being passed is an array of accounts with the prop name as "accounts"

export class AllAccountsLoaded implements Action {
  readonly type = AccountActionTypes.AllAccountsLoaded;

  constructor(public payload: {accounts: Account[]}) {
  }
}

So it seems the payload should be passed correct. what worries me is the part of the error: '{ account: Account; } | { accounts: Account[]; }'. I already saw Ngrx throwing that kind of errors if I would mistype changing one of the events name in the effects observable but I've checked it and it looks fine first view:

  @Effect()
  loadAllAccounts$ = this.actions$
  .pipe(
    ofType<AllAccountsRequested>(AccountActionTypes.AllAccountsRequested),
    withLatestFrom(this.store.pipe(select(allAccountsLoaded))),
    filter(([action, allAccountsLoaded]) => !allAccountsLoaded),
    mergeMap(() => this.accountService.getUserAccounts()),
    map(accounts => new AllAccountsLoaded({accounts}))
  );

Solution

  • Ok. If anyone else might run into this. The error message is very misleading. If you take a look at the reducer definition:

    export function accountReducer(state = initialAccountState, action: AccountActions): AccountState {
      switch(action.type) {
        case AccountActionTypes.AccountLoaded:
          adapter.addOne(action.payload.account, state);
    
        case AccountActionTypes.AllAccountsLoaded:
          adapter.addAll(action.payload.accounts, {...state, allAccountsLoaded: true});
        default: {
          return state;
        }
      }
    }
    

    You will notice that near the adapters repository style functions you are missing a return clause, so it should be:

    return adapter.addAll(action.payload.accounts, {...state, allAccountsLoaded: true});
    

    It's just a typo but one which might take you a while to track down because of the error pointing you in a completely different direction.