NgRX effects - Type 'Observable<unknown>' is not assignable to type 'Observable<Action>'
Asked Answered
L

12

70

While working with NgRX 8 my colleagues and me are frequently facing a weird error message when implementing the effects.

Type 'Observable<unknown>' is not assignable to type 'Observable<Action> | ((...args: any[]) => Observable<Action>)'

It is related to type issues. It is really annoying that the message is so unspecific and it marks the complete effect. This appears frequently and is really hard to resolve.

enter image description here

We are wondering if there is something we can do in order to quickly identify the problems or if we are able to destructure the message in some way. I am not looking for a specific solution here, but more for a "how to quickly determine what's wrong"-procedure.

Thanks and cheers!

Collection of possibilities

It is what we have done so far and also ideas from comments and answers.

  • In most cases it is probably not an issue with actions
  • It can be a wrong destructuring for the switchMap parameter
  • One of the RxJS operators is not imported
  • Wrong parameters and return value in the service can be the reason
  • Inconsistent types in service and action can be the reason as well
  • If nothing helps and you are sure, there is nothing wrong: Reload TypeScript

We are still hoping for a trick to break down the error message.

Lye answered 29/7, 2019 at 5:25 Comment(9)
what is addComment ?Episiotomy
What is the type of addCommentFailed / of(addCommentFailed)?Bartz
It is not important to solve this issue, but to get somehow a general method to proceed with this error. addComment is the return of the createAction method and is probably not the issue. Also the failed and success methods are createAction-generated actions. The failed is not the problem here, although I do not execute the failed action as a function.Lye
I recreated it locally and don't get any error. Did you check if your editor workspace uses the projects typescript version? Sometimes VSCode starts using the globally installed TS version.Bartz
In the ofType parameter, you need pass it as ${your action}.type, If the action is created via createActionDanyel
@Danyel no I do not need to do that, it works with other effects like this.Lye
@cyr-x this is what helped with a prior problem and what I do regularly if nothing helps with type errors. Good point.Lye
Also, the Reload typescript project command or an editor restart helps sometimes, especially if huge projects are open for a long time. But I guess that's not the problem here.Bartz
Yup sorry. It was during the initial phase of V8 release, we had to use type parameter.Danyel
L
120

Quick version
comment out createEffect(() =>,
fix errors that your IDE (VSCode) flags up,
add createEffect(() => back in.

Alternative - rewriting like the following also works

someEffect$ = createEffect(() => {
  return this.actions$.pipe(
    ...
  )
})

Additional

Still errors after doing the above? Type-checking is doing it's job correctly and telling you that you should be mapping to an Observable<Action> or for a purely side-effect effect adding the second argument { dispatch: false } (i.e. not dispatching an action). See the NgRx Effects Docs


Older Answer (using @Effect is unneccessary and is not required)

The easiest way I've found to debug is to write in a version 7 manner with the @Effect decorator and once done rewrite using createEffect.

So to debug:

  navigateToDashboard$ = createEffect(() =>
    this.actions$.pipe(
      ofType(teamActions.CREATE_SUPERVISOR_GROUP_SUCCESS),
      map((action: teamActions.CreateSupervisorGroupSuccess) => action.payload),
      map((team: Team) => team.TeamID),
      SwitchMap(id => new routerActions.Go({ path: ['/team', id, 'populate'] }))
    )
  )

which gives the non-helpful error write as (add decorator, delete createEffect(() =>, delete final bracket),

@Effect()
navigateToDashboard$ = this.actions$.pipe(
    ofType(teamActions.CREATE_SUPERVISOR_GROUP_SUCCESS),
    map((action: teamActions.CreateSupervisorGroupSuccess) => action.payload),
    map((team: Team) => team.TeamID),
    SwitchMap(id => new routerActions.Go({ path: ['/team', id, 'populate'] }))
)

Now we get error

Cannot find name 'SwitchMap'

Followed by

Type 'Go' is not assignable to type 'ObservableInput<any>'

Fixing this gives

@Effect()
navigateToDashboard$ = this.actions$.pipe(
    ofType(teamActions.CREATE_SUPERVISOR_GROUP_SUCCESS),
    map((action: teamActions.CreateSupervisorGroupSuccess) => action.payload),
    map((team: Team) => team.TeamID),
    switchMap(id => of(new routerActions.Go({ path: ['/team', id, 'populate'] })))
)

Now rewrite in NgRx 8 terms. Not pretty but works.

Lanti answered 22/8, 2019 at 15:0 Comment(7)
Commenting out createEffect(() => works flawlessly, thanks a lot!Lye
@ngfelixl not quite flawlessly, if I put my project into a stricter typescript mode ("strict": true in tsconfig.json) then this approach does not always work.Lanti
Agreed, but I mean flawlessly compared to the original problem.Lye
You just saved me a lot of head-scratching. Thank you.Remark
does anyone had same error with 'switchMap/MergeMap' even after fix of the createEffect error?Birnbaum
@Birnbaum see the Additional note above - very likely you're mapping to a non-action and not including { dispatch: false }. If you're still struggling, ask a new questionLanti
zomg.... the completely useless errors displayed while making effects was one of my biggest headaches on the planet.. thanksTactics
A
17

I had the exact same issue and in my case it was because of wrongly placed braces and a missing import.

Here is what I did to debug and solve it.

Split the inner pipe-able function into individual functions. This is the foremost step for me because of the complex syntax and auto complete braces from vscode, sometimes a brace exists in wrong place and it's not easy to find. This also solves almost all other problems (missing imports, incorrect return types etc) as the code to debug is much smaller and vscode highlights this individual error from the sub function.

This is what I had before

    performLogin$ = createEffect(() => 
       this.actions$.pipe(
           ofType(performLogin),
           mergeMap(() => this.loginService.performLogin().pipe(
               map(() => loginSuccess())
           )))
    );

Changed this to below

 performLogin$ = createEffect(() => 
       this.actions$.pipe(
           ofType(performLogin),
           mergeMap(() => this.performLogin()),
           catchError((error ) => { console.log("HELLO"); return EMPTY})
       )
    );

    performLogin() {
        return this.loginService.performLogin()
        .pipe(map(() => loginSuccess()));
    }

Also a bonus I got from this approach is that the catchError block on the inner pipe does not get triggered (as per effects example it should work). Hence had to include it in the outer pipe-able block. Here the error is caught and works as expected. But still figuring out why it does not work.

Just to sum it up, the login service does the following (throw error or return Observable of true)

//login.service.ts

performLogin() : Observable<boolean> {
        throw "Something went wrong";
        //return of(true);
    }

Hope this helps.

Allonge answered 4/8, 2019 at 15:58 Comment(2)
Hi, thanks for your answer, I am going to test your strategy today and let you know :)Lye
Thank you so much you saved me so much time! :)Ironhanded
G
14

In case of dealing with this problem and using official ngrx 8 example.

loadMovies$ = createEffect(() => this.actions$.pipe(
    ofType('[Movies Page] Load Movies'),
    mergeMap(() => this.moviesService.getAll()
      .pipe(
        map(movies => ({ type: '[Movies API] Movies Loaded Success', payload: movies })),
        catchError(() => EMPTY)
      ))
   )
);

Easy and fast solution can be putting "any" type.

loadMovies$: any = createEffect((): any => this.actions$.pipe(
    ofType('[Movies Page] Load Movies'),
    mergeMap(() => this.moviesService.getAll()
      .pipe(
        map(movies => ({ type: '[Movies API] Movies Loaded Success', payload: movies })),
        catchError(() => EMPTY)
      ))
   )
);

Do not forget imports for rxjs operators, observables.

In case of dealing with action payload - props, define an action type.

ofType<MySuperActions>

or

ofType<ReturnType<typeof myAction>>
Graupel answered 12/1, 2020 at 18:49 Comment(0)
T
4

What fixed it for me was adding Observable<Action> as the return type for the function that's passed into createEffect. Ex:

addComment$ = createEffect((): Observable<Action> =>
  this.actions$.pipe(
    ...
  ),
);
Tavel answered 27/10, 2021 at 19:17 Comment(0)
D
2

Actually you need to treat actions created by createAction as a function and call it to return the action object. Check this desc. So your of(addCommentFailed) should be of(addCommentFailed()).

Danyel answered 29/7, 2019 at 7:22 Comment(3)
Thanks for your answer, I know that I have to execute the function but it is not causing the error. As I mentioned in the comments before, I am also not looking for a specific solution of the screenshot but a general "quickly determine what's wrong"-instruction.Lye
Ok. Now I get it. But I Believe there can be no one general instruction. I would suggest to with the created actions return types. This suggestion is based on this test spec, you can see a similar error message being asserted.Also check thisDanyel
I believe this to be the correct answer. your catchError method is returning the function and not the value of the function. Adding parens to all actions is required in NgRX 8.Kidd
M
2

I had this issue because of missing import 'of' operator here

// events.effects.ts
...

getEvents$: Observable<Action> = createEffect(
    () => this.actions$.pipe(
      ofType(EventsActions.getEvents),
      switchMap(action =>
        this.eventService.getEvents().pipe(
          map(events => EventsActions.getEventsSuccess({ events })),
          catchError(error => of(EventsActions.getEventsError({ error })))
        )
      )
    )
  );
...

I've fixed it by adding this line of code at the top

// events.effects.ts

import { Observable, of } from 'rxjs';

...
Manning answered 14/12, 2019 at 12:6 Comment(1)
I agree, this error appears if one of the operators or observable "constructors" are not imported. But it also appears if types are not matching and in several other cases. Thanks a lot for your answer and I think there is someone who is interested in your solution. :)Lye
D
0

I ran into a similar issue today (same error message) and it was because TypeScript couldn't infer correctly the return type of Array#flatMap. I assume it will be the same for Array#map, RxJS#map or any array that has not been explicitly typed.

Here is the code that crashed :

this.actions$.pipe(
    ofType(ActionType),
    switchMap((action) => {
        return action.cart.flatMap((cartItem: CartItem) => {
            if (x) {
                return [
                    ActionCreator1(params1),
                    ActionCreator2(params2)
                ];
            } else {
                return [];
            }
        }
    })
)

So I got the same error message :

Type 'Observable<unknown>' is not assignable to type 'Observable<Action> | ((...args: any[]) => Observable<Action>)'

To fix it, I just had to tell TypeScript that my mapping function returned an array of Action :

import { Action } from '@ngrx/store';
...
    switchMap((action) => {
        return action.cart.flatMap((cartItem: CartItem) : Action[] => {
...

Also, typing with Action[] is safer that with any !

Diner answered 6/2, 2020 at 14:25 Comment(0)
C
0

In my case, I used a lodash operator inside one of the rxjs operators. Turned out, I didn't have @types/lodash. After npm i -D @types/lodash, my problem was solved.

Carpology answered 13/3, 2020 at 18:59 Comment(0)
C
0

I struggled with this issue for a while only to find out at the end that VS code automatically imported Observable from some library instead of rxjs

Hope this saves someone unnecessary head banging.

Curvature answered 26/11, 2020 at 8:29 Comment(0)
H
0

In my case I was using error function. After removing that error vanished.

before:

    loadLocations$ = createEffect(() => this.action$.pipe(
        ofType(loadLocationsAction),
        switchMap(
            () => this.service.loadLocations().then(
                (response: any) => {
                    return loadLocationsSuccessAction({locations: response.items});
                },
                error => {
                    console.log(error);
                }
            )
        )
    ));

After:

    loadLocations$ = createEffect(() => this.action$.pipe(
        ofType(loadLocationsAction),
        switchMap(
            () => this.service.loadLocations().then(
                (response: any) => {
                    return loadLocationsSuccessAction({locations: response.items});
                }
            )
        )
    ));
Hidden answered 15/3, 2023 at 2:8 Comment(0)
L
-1

I have seen similar error as well.

Type 'Observable<unknown>' is not assignable to type 'Observable<Action> | ((...args: any[]) => Observable<Action>)'

The quickest way I identify what may have happened is after | separator. The type is usually Observable<{something}> or Observable<{something | another}>. There is a | separator and the second item is not an Observable. This is maybe the problem.

If for example the observable expects to have Observable<Action>, but there is some pipe operator returns something other than Observable<Action>. The unexpected type will appended to the expected type in the error message, because for Observable, T does not exist in ((...args: any[]) => Observable<Action>)

my interpretation of the message is

Internal type of an object is unknown, because we do not know which type to expect Observable<Action> = Action or ((...args: any[]) => Observable<Action>) = unknown

if the problem is not immediately obvious, I just comment out the each pipe operator one by one and see if error goes way. If it does, now I know this operator is the problem, than I focus on operator and find the problem.

I am interested to read other users replys. Those two way always points me in the right direction.

Lalapalooza answered 4/8, 2019 at 0:47 Comment(1)
Hi @mr.vea, thanks for your answer :) I am going to have a look at this today.Lye
V
-1

I had this error in my project and solved it by adding this import:

import { Observable, of as observableOf, of } from 'rxjs';
Valerianaceous answered 14/6, 2021 at 12:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.