Search code examples
javascriptangulartypescriptrxjsngrx

NGRX Effect withLatestFrom Typescript error ts2345 troubleshooting


I'm not sure how to continue troubleshooting this ts2345 error and could use some help with understanding its cause.

Error Detail

Argument of type '(source$: Observable) => Observable' is not assignable to parameter of type 'OperatorFunction'.

Types of parameters 'source$' and 'source' are incompatible.

import("c:/Users/Flignats/Documents/GitHub/reqloot/node_modules/rxjs/internal/Observable").Observable' is not assignable to type 'import("c:/Users/Flignats/Documents/GitHub/reqloot/node_modules/rxjs/internal/Observable").Observable'.

Property 'user' is missing in type 'import("c:/Users/Flignats/Documents/GitHub/reqloot/src/app/modules/guilds/guilds.state").State' but required in type 'import("c:/Users/Flignats/Documents/GitHub/reqloot/src/app/modules/user/user.state").State'.ts(2345)

user.state.ts(19, 3): 'user' is declared here.

This is my effect file in the 'guilds' feature

guilds.effects.ts

import { IPrivateGuildDetails } from './guilds.model';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { select, Store } from '@ngrx/store';
import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store';
import { Actions, ofType, createEffect } from '@ngrx/effects';
import { of } from 'rxjs';
import {
    map,
    mergeMap,
    catchError,
    withLatestFrom,
    switchMap,
    tap,
    filter
} from 'rxjs/operators';
// Core
import * as fromCore from '../../core';
import * as fromAuth from '../../core/auth/auth.selectors';
import * as fromUser from '../user/user.selectors';
// Key
export const GUILDS_KEY = 'GUILDS';
// State
import { State, selectGuilds } from './guilds.state';
// Actions
import {
    createGuild,
    createGuildSuccess,
    createGuildFailure,
    loadPublicGuilds,
    loadPublicGuildsSuccess,
    loadPublicGuildsFailure,
    loadTriggerStatusCreateGuild,
    loadTriggerStatusCreateGuildFailure,
    loadTriggerStatusCreateGuildSuccess,
    selectPublicGuild,
    loadPublicGuild,
    loadPublicGuildSuccess,
    loadPublicGuildFailure,
    loadMyGuild,
    loadMyGuildSuccess,
    loadMyGuildFailure
} from './guilds.actions';

@Injectable()
export class GuildsEffects {
    loadMyGuild$ = createEffect(() =>
        this.actions$.pipe(
            ofType(loadMyGuild),
            withLatestFrom(this.store.pipe(select(fromUser.selectUserAccountDetails))),
            switchMap(([action, user]) =>
                this.afs.loadMyGuild(user).pipe(
                    map((result: IPrivateGuildDetails) => loadMyGuildSuccess({ myGuild: result })),
                    catchError(error => of(loadMyGuildFailure({ error })))
                )
            )
        )
    );

    loadTriggerStatusCreateGuild$ = createEffect(() =>
        this.actions$.pipe(
            ofType(loadTriggerStatusCreateGuild),
            withLatestFrom(this.store.pipe(select(fromAuth.selectUid))),
            mergeMap(([action, uid]) =>
                this.afs.getTriggerStatusCreateNewGuild(uid).pipe(
                    map(triggerStatus =>
                        loadTriggerStatusCreateGuildSuccess({ triggerStatus })
                    ),
                    catchError(error =>
                        of(loadTriggerStatusCreateGuildFailure({ error }))
                    )
                )
            )
        )
    );

  constructor(
    private actions$: Actions,
    private afs: fromCore.FirestoreService,
    private localStorageService: fromCore.LocalStorageService,
    private router: Router,
    private store: Store<State>
  ) {}
    }

guilds.state.ts

import { ActionReducerMap, createFeatureSelector } from '@ngrx/store';
import { AppState } from '@app/core';

import { guildsReducer } from './guilds.reducer';
import { GuildsStateDetails } from './guilds.model';

export const FEATURE_NAME = 'guilds';

export interface GuildsState {
  state: GuildsStateDetails;
};

export const reducers: ActionReducerMap<GuildsState> = {
  state: guildsReducer
};

export const selectGuilds = createFeatureSelector<State, GuildsState>(FEATURE_NAME);

export interface State extends AppState {
  guilds: GuildsState;
};

The error is produced in the loadMyGuild$ withLatestFrom select fn

The loadTriggerStatusCreateGuild$ effect is using a select from the core module and it does not produce an error.

If I update the loadMyGuild$ withLatestFrom selector with a selector from the Guilds module, i.e. selectGuilds there is no error.

I'm unsure what could be different in the User feature module that is causing this error when trying to use its selectors in the Guilds feature module.

user.state.ts

import { ActionReducerMap, createFeatureSelector } from '@ngrx/store';
import { AppState } from '@app/core';

import { userReducer } from './user.reducer';
import { UserStateDetails } from './user.model';

export const FEATURE_NAME = 'user';
export const selectUser = createFeatureSelector<State, UserState>(FEATURE_NAME);

export const reducers: ActionReducerMap<UserState> = {
  state: userReducer
};

export interface UserState {
  state: UserStateDetails;
}

export interface State extends AppState {
  user: UserState;
}

user.selectors.ts

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

import { UserState, selectUser } from './user.state';

export const selectActiveUser = createSelector(
  selectUser,
  (state: UserState) => state.state
);

export const selectUserAccountDetails = createSelector(
  selectUser,
  (state: UserState) => state.state.account
);

export const selectUserLoading = createSelector(
  selectUser,
  (state: UserState) => state.state.loading
);

export const selectUserError = createSelector(
  selectUser,
  (state: UserState) => state.state.error
);

user.model.ts

import { HttpErrorResponse } from '@angular/common/http';
import { Timestamp } from '@firebase/firestore-types';

export interface IUser {
  applixir?: {
    lastWatchedAt?: Timestamp;
    staminaLastGiven?: Timestamp;
    totalAdsWatched?: number;
    totalStaminaCollected?: number;
    currentAdsTrackedCount: number;
  };
  balance: {
    claimed: number;
    unclaimed: number;
  };
  createdAt: Timestamp;
  displayName?: string;
  email: string;
  fcmTokens?: { [token: string]: true };
  guildDetails?: {
    attacks: { [key: string]: number };
    boosts: { [key: string]: { [key: string]: number } };
    guildId: string;
    guildTokensCollected: number;
    guildTokensCollectedCount: number;
    shield: { [key: string]: number };
  };
  referredBy?: string;
  referredAt?: Timestamp;
  stamina: number;
  staminaLastGiven?: Timestamp;
  subscriberStamina?: number;
  subscriberStaminaLastGiven?: Timestamp;
  uid: string;
  updatedAt?: Timestamp;
}

export interface UserStateDetails {
  error: HttpErrorResponse | string;
  loading: boolean;

  account: IUser;
}

Solution

  • You are using wrong store - you injected in your effects Store<State> where State is guild state and of course user selectors won't work on it. You need to inject Store with User state and use it instead.