NgRX效果 - 类型“Observable<unknown>”无法赋值给类型“Observable<Action>”。

69
在使用NgRX 8时,我和我的同事们经常会遇到一个奇怪的错误消息,当我们实现效果时会出现这个错误消息。
“类型'Observable '不可分配给类型'Observable | ((...args: any[]) => Observable )'”
这与类型问题有关。令人讨厌的是,这个消息非常不具体,而且标记了整个效果。这种情况经常发生,很难解决。

enter image description here

我们想知道是否有办法快速识别问题,或者我们能否以某种方式解构消息。 我这里不是在寻找特定的解决方案,而是更多地寻求“如何快速确定问题”的程序。
谢谢!
可能性的收集
这是我们迄今为止所做的以及来自评论和答案的想法。
  • 在大多数情况下,问题可能不在于操作
  • 可能是switchMap参数的错误解构
  • 其中一个 RxJS 操作符未被导入
  • 服务中的错误参数和返回值也可能是原因
  • 服务和操作之间不一致的类型也可能是原因
  • 如果没有帮助,并且您确定没有问题:重新加载 TypeScript
我们仍然希望有技巧可以分解错误消息。

什么是 addComment - Yanis-git
解决此问题并不重要,重要的是获得一种通用方法以处理此错误。 addCommentcreateAction 方法的返回值,可能不是问题所在。 而 failedsuccess 方法是由 createAction 生成的操作。 在这里,failed 不是问题,尽管我没有将其作为函数执行。 - Felix Lemke
1
我在本地重新创建了它,没有出现任何错误。你检查一下你的编辑器工作区是否使用了项目的 TypeScript 版本了吗?有时候 VSCode 会开始使用全局安装的 TS 版本。 - cyr_x
在ofType参数中,你需要将其作为${your action}.type传递,如果该操作是通过createAction创建的。 - KiraAG
此外,“重新加载 TypeScript 项目”命令或编辑器重启有时会有所帮助,特别是当打开大型项目很长时间时。但我猜这不是问题的原因。 - cyr_x
显示剩余4条评论
12个回答

118

简化版
createEffect(() =>注释掉,
修复你的IDE(VSCode)标记出的错误,
再将createEffect(() =>添加回去。

另一种方式 - 以下重写也可以工作

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

另外提示

以上操作后仍然存在错误吗? 类型检查正在正确地执行它的工作,并告诉您应该映射到 Observable<Action> 或者作为纯副作用效果添加第二个参数 {dispatch: false}(即不分派动作)。请参阅 NgRx Effects 文档


旧回答(使用 @Effect 是不必要的,也不是必需的)

我发现调试最简单的方法是以版本7方式编写代码,使用@Effect装饰器,完成后再使用createEffect重新编写。

所以要调试:

  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'] }))
    )
  )

这会生成不太有用的错误,提示你添加一个装饰器,并删除 createEffect(() =>,最后删除最后一个括号。

@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'] }))
)

现在我们遇到了错误

Cannot find name 'SwitchMap'

随后

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

修复这个问题会给出:

@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'] })))
)

现在用NgRx 8的术语进行重写。不太美观但有效。


15
createEffect(() => 注释掉后,一切都运行得非常好,非常感谢! - Felix Lemke
1
@ngfelixl,如果我将我的项目放入更严格的 TypeScript 模式(在 tsconfig.json 中设置 "strict": true),那么这种方法并不总是有效的。 - Andrew Allen
1
你刚刚帮我省去了很多烦恼。谢谢你。 - Jan Klan
有人在修复了createEffect错误后,仍然遇到'switchMap/MergeMap'的相同错误吗? - Zaben
@Zaben 请注意上面的附加说明 - 很可能你正在映射到一个非操作,并且没有包含 { dispatch: false }。如果你仍然遇到困难,请提出一个新的问题。 - Andrew Allen
显示剩余2条评论

17

我遇到了完全相同的问题,原因是括号放错位置和缺少导入。

以下是我用来调试和解决它的步骤。

将内部可管道的函数拆分成单独的函数。对我来说,这是首要步骤,因为由于vscode的复杂语法和自动完成括号,有时括号存在错误的位置,而且不容易找到。这也解决了几乎所有其他问题(缺失导入、不正确的返回类型等),因为要调试的代码要小得多,而vscode会突出显示子函数中的个别错误。

这就是我之前的代码:

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

将此更改为以下内容

 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()));
    }

此方法的额外收益是内部管道中的catchError块不会被触发(按照效果示例应该起作用)。因此,必须将其包含在外部可管道块中。在这里,错误被捕获并按预期工作。但仍在弄清为什么它不起作用。

简单来说,登录服务执行以下操作(抛出错误或返回Observable true)。

//login.service.ts

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

希望这有所帮助。

嗨,谢谢你的回答,我今天会测试你的策略并告诉你 :) - Felix Lemke
非常感谢,你帮我节省了很多时间! :) - Jimmy Geers

14

如果遇到这个问题并使用官方ngrx 8示例进行处理。

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)
      ))
   )
);

简单而快速的解决方案可以放置“任何”类型。

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)
      ))
   )
);

不要忘记引入rxjs的操作符和Observables。

在处理动作负载(props)时,定义一个动作类型。

ofType<MySuperActions>
或者
ofType<ReturnType<typeof myAction>>

4
我的问题被解决了,关键是将传递到createEffect的函数的返回类型设置为Observable<Action>。例如:
addComment$ = createEffect((): Observable<Action> =>
  this.actions$.pipe(
    ...
  ),
);

2
实际上,您需要将由createAction创建的操作视为函数并调用它以返回操作对象。请参阅此说明。因此,您的of(addCommentFailed)应该是of(addCommentFailed())。"addCommentFailed"翻译成"添加评论失败"。

谢谢您的回答,我知道我必须执行该函数,但它并没有导致错误。正如我之前在评论中提到的,我也不是在寻找屏幕截图的特定解决方案,而是一个通用的“快速确定问题所在”的指令。 - Felix Lemke
好的。现在我明白了。但我认为没有一个通用的指令。我建议根据创建的操作返回类型进行设置。这个建议基于这个测试规范,你可以看到一个类似的错误消息被断言。还要检查这个 - KiraAG
我相信这是正确的答案。你的 catchError 方法返回的是函数,而不是函数的值。在 NgRX 8 中,需要对所有操作添加括号。 - Travis Peterson

2

因为缺少 'of' 操作符,导致出现了这个问题。

// 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 })))
        )
      )
    )
  );
...

我已经通过在顶部添加这行代码来解决了它。
// events.effects.ts

import { Observable, of } from 'rxjs';

...

我同意,如果其中一个运算符或可观察的“构造函数”未被导入,则会出现此错误。但是,如果类型不匹配以及其他几种情况也会出现此错误。非常感谢您的答案,我认为有人对您的解决方案感兴趣。 :) - Felix Lemke

0
在我的情况下,我正在使用错误函数。在删除了该错误后,错误消失了。
之前:
    loadLocations$ = createEffect(() => this.action$.pipe(
        ofType(loadLocationsAction),
        switchMap(
            () => this.service.loadLocations().then(
                (response: any) => {
                    return loadLocationsSuccessAction({locations: response.items});
                },
                error => {
                    console.log(error);
                }
            )
        )
    ));

之后:

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

0
我曾为此问题苦苦挣扎,最终才发现 VS Code 自动从某个库中导入了Observable,而不是rxjs
希望这可以帮助其他人避免不必要的头痛。

0
在我的情况下,我在rxjs操作符中使用了一个lodash操作符。结果发现,我没有安装@types/lodash。经过npm i -D @types/lodash的安装后,我的问题得到了解决。

0
今天我遇到了一个类似的问题(同样的错误信息),原因是 TypeScript 无法正确推断Array#flatMap的返回类型。 我想Array#mapRxJS#map或任何未明确类型化的数组都可能会出现相同的问题。

这是导致程序崩溃的代码:

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

所以我得到了相同的错误信息:

类型“Observable<unknown>”不能分配给类型“Observable<Action> | ((...args: any[]) => Observable<Action>)”

为了解决这个问题,我只需要告诉 TypeScript 我的映射函数返回一个 Action 数组即可:

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

此外,使用 Action[] 进行输入比使用 any 更加安全!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接