如何说服打字稿可以在主题上调用Observable运算符?

mac*_*tyr 5 rxjs typescript rxjs5 angular

使用Angular 4.1.3,rxjs 5.3.0,打字稿2.2.2的示例代码:

import { Subject } from 'rxjs'

export class Example {
  public response$: Subject<boolean>;

  public confirm(prompt: string): Subject<boolean> {
    // ...set up a confirmation dialog...
    this.response$ = new Subject<boolean>();
    return this.response$.first();
  }
}
Run Code Online (Sandbox Code Playgroud)

当我尝试编译此代码时,打字稿抱怨最后一行:

The 'this' context of type 'Subject<boolean>' is not assignable to method's 'this' of type 'Observable<boolean>'.
  Types of property 'lift' are incompatible.
    Type '<R>(operator: Operator<boolean, R>) => Observable<boolean>' is not assignable to type '<R>(operator: Operator<boolean, R>) => Observable<R>'.
  Type 'Observable<boolean>' is not assignable to type 'Observable<R>'.
    Type 'boolean' is not assignable to type 'R'.
Run Code Online (Sandbox Code Playgroud)

我的意思是说这first()是一种可观察的方法,而不是主题,但是我的理解是,主题也是一种可观察的方法,所以这应该不是问题。而且,实际上,如果我忽略该错误,则代码可以编译并正常运行。

我尝试过的事情:

  1. first显式导入运算符的各种方式,例如import 'rxjs/add/operator/first'。这不会更改错误。
  2. 铸造主题,以可观察到的前调用first(),例如return (this.response$ as Observable<boolean>).first();。这导致一个不同但相似的错误,对我来说似乎也不正确:Type 'Observable<boolean>' is not assignable to type 'Subject<boolean>'. Property 'observers' is missing in type 'Observable<boolean>'.

如何说服打字稿调用first()主题是有效的?

为了响应下面对返回类型的讨论而澄清:该方法可能还具有不正确的返回类型(应该是可观察的,而不是主题),但这似乎是一个单独的问题;更改返回类型不能解决上述错误。确认这一点的进一步细节是,当vscode突出显示错误时,它仅突出显示this.response$,而不是整个返回行,这表明问题出在this.response$和之间first(),而不是在first()函数签名之间:

错误在vscode中突出显示

ggr*_*nig 2

TL;DR: TypeScript 2.4 中引入的更严格的类型检查与 RxJS < 5.4.2 不兼容(由于错误)。您的 IDE 可能使用 TypeScript >= 2.4 进行类型检查。升级 RxJS(推荐)、降级 TypeScript,或者将编程解决方案Subject.asObservable与返回类型一起使用Observable<boolean>

解释:

讨论结束后,我查看了 RxJS 5.3.0 源代码,发现和之间的签名lift不同。具体来说,我们可以看到以下区别:SubjectObservable

主题.ts:

lift<R>(operator: Operator<T, R>): Observable<T>
Run Code Online (Sandbox Code Playgroud)

可观察的.ts:

lift<R>(operator: Operator<T, R>): Observable<R>
Run Code Online (Sandbox Code Playgroud)

返回的 Observable 的通用类型是T in Subject,但R in Observable

first()运算符需要 anObservable作为其上下文,但由于 的签名this不匹配,因此无法匹配。这对我来说很奇怪,因为它扩展了,因此它的所有属性都应该有匹配的签名。SubjectliftSubjectObservable

现在这里是解决方案:TypeScript 在 2.4 版本中引入了更严格的类型检查,实际上 RxJS 需要通过更改方法的签名来赶上这一变化lift。此更改可以在版本5.4.2的更改日志中看到:

主题:提升签名现在适用于更严格的 TypeScript 2.4 检查

这是对应的问题。这似乎是一个错误。

您正在运行 RxJS 5.3,所以这就是您收到错误的原因。

有两种选择。升级 RxJS(推荐)或使用Subject.asObservable将类型从 更改SubjectObservable.

正如我的评论中提到的,函数的返回类型需要更改为Observable<boolean>

应用于您的剪裁,应该如下所示:

public confirm(prompt: string): Observable<boolean> {
    // ...set up a confirmation dialog...
    this.response$ = new Subject<boolean>();
    return this.response$.asObservable().first();
  }
Run Code Online (Sandbox Code Playgroud)

我看到您的 TypeScript 版本是2.2,但您的 IDE 很可能使用另一个 TypeScript 版本进行类型检查。

选择

正如@cartant提到的,Subject.lift实现返回一个Subject,但是不能用 TypeScript 的类型来表达(没有返回类型重载,甚至不确定是否有支持它的语言)。

因此,另一个解决方案是通过强制转换为 来忽略类型检查<any>。这样你也可以回来Subject