0

I have a simple goal of creating an observable that emits a list of all the countries in the world. The type of the observable is Country[], i.e. the store value is typed as an array of objects. However, when passed to switchMap, it magically gets converted into type Country. I have no idea what causes this behavior and if it is not, by chance, on my side. Here is the function in question:

public getAllCountries(): Observable<Country[]> {

      const getAppState = createFeatureSelector<ServicesState>('services');
      const getCountries = createSelector( getAppState, (state: ServicesState): Country[] => state.countries);
      // as you can see, this is the NGRX store slice that must be of type Observable<Country[]>
      const countriesFromStore$ = this.store.pipe(select(getCountries));

      return countriesFromStore$.pipe(
        // this is the switchMap that causes the error
        switchMap( countries => {
        // this is the return statement that somehow converts an array of objects into a single object??
        if (countries)  { return countries; }
        const countriesFromServer$: Observable<Country[]> = this.http.get<FilteredArrayResponse<Country>>
           (this.baseUrl, this.config.httpGetJsonOptions).pipe(
               tap( result => this.store.dispatch( new LoadCountriesAction(result.data)) ),
               map( result => result.data)
           );
        return countriesFromServer$; })
    );
  }

In case you have questions :

  • FilteredArrayResponse is a generic interface with data attribute actually containing the array
  • The full error message is Type 'Observable<Country | Country[]>' is not assignable to type 'Observable<Country[]>'.
  • My guess is that somehow switchMap confuses an Observable of Arrays with an Array of Observables...
  • The return type of result.data is always an array
  • Other operators from the map family exhibit the same behaviour
petajamaja
  • 520
  • 2
  • 9
  • 26
  • If you're using Visual Studio Code, hover over `countriesFromStore$` to see its type and ensure it is `Observable`. On the `switchMap`, hover over `countries` and make sure it is `Country[]`. I am thinking the problem lies in `const countriesFromServer$: Observable` where the right side of it can be `Observable`. Remove the cast of `: Observable` on `const countriesFromServer$` and hover over it and see if you see `Observable`. If so, this is the issue. You will have to go into `` for what it is. – AliF50 Mar 04 '20 at 12:57
  • @AliF50 I checked the types and yes I am using VSCode and it shows all the correct types. When I comment the server request with `countriesFromServer$` out overall, the problem still persisits but becomes `Type 'Observable' is not assignable to type 'Observable'`, so it is indeed localized to the switchMap. – petajamaja Mar 04 '20 at 13:01
  • 2
    If you return an array in `switchMap`'s projection function it will iterate it and emit each value as a separate emission (the same applies for `mergeMap`, `concatMap`, etc..). That's why the type is `Country[] | Country`. Use `of()` as @AliF50 suggests bellow. – martin Mar 04 '20 at 13:33
  • thanks @martin, I am trying to! – petajamaja Mar 04 '20 at 13:35

1 Answers1

4

Try returning of(countries) in the switchMap (import { of } from 'rxjs';). I think that should fix it. The switchMap needs to switch to an Observable.

import { of } from 'rxjs';
...
switchMap((countries: Country[]) => {
  if (countries) {
    return of(countries);
  }
  ....
})
AliF50
  • 16,947
  • 1
  • 21
  • 37
  • `Type 'Observable' is not assignable to type 'Observable'.` – petajamaja Mar 04 '20 at 13:11
  • Thanks for explaining that the switchMap return statements should always return observables, not just plain values. I got it confused. Gonna keep heading in this direction! – petajamaja Mar 04 '20 at 13:17
  • No problem, I edited my answer by including `countries: Country[]` in the `switchMap`. Somewhere along the lines you are losing `Country[]` and it is turning into `unknown`. – AliF50 Mar 04 '20 at 14:16