我怎样才能让 typescript 相信可以在 Subject 上调用 Observable 运算符?

How can I convince typescript that it's ok to call Observable operators on a Subject?

示例代码,使用 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();
  }
}

当我尝试编译这段代码时,typescript 抱怨最后一行:

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'.

我的感觉是说first()是Observable的方法,不是Subject的方法,但是我的理解是Subject也是Observable,所以这个应该问题不大。而且,事实上,如果我忽略错误代码编译和运行正常。

我尝试过的事情:

  1. 显式导入 first 运算符的各种方法,例如import 'rxjs/add/operator/first'。这不会更改错误。
  2. 在调用 first() 之前将 Subject 转换为 Observable,例如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>'.

如何让 typescript 相信在 Subject 上调用 first() 是有效的?

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

您对 Subject 也是 Observable 的理解是正确的,但在某种程度上 Subject 也可以用作 Observable。但这并不能使 Subject 成为 Observable,反之亦然。

按照 ggradnig 的建议,您应该将 confirm 函数的 return 类型更改为 Observable<boolean> 而不是 Subject<boolean>,因为 first 方法在Subject<boolean> 会 return 一个 Observable<boolean>

import { Subject } from 'rxjs'

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

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

TL;DR: TypeScript 2.4 中引入的更严格的类型检查与 RxJS < 5.4.2 (due to a bug) 不兼容。您的 IDE 可能正在使用 TypeScript >= 2.4 进行类型检查。升级 RxJS(推荐),降级 TypeScript,或者使用编程解决方案 Subject.asObservable 和 return 类型 Observable<boolean>.

解释:

经过讨论,我查看了 RxJS 5.3.0 源代码,发现 lift 的签名在 SubjectObservable 之间有所不同。具体可以看出以下区别:

Subject.ts:

lift<R>(operator: Operator<T, R>): Observable<T>

Observable.ts:

lift<R>(operator: Operator<T, R>): Observable<R>

returned Observable 的通用类型在 Subject 中是 T,但在 Observable

first() 运算符的 this 上下文需要 Observable,由于 [=15= 的签名不匹配,Subject 无法匹配].这对我来说很奇怪,因为 Subject 扩展了 Observable,因此它的所有属性都应该有匹配的签名。

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

Subject: lift signature is now appropriate for stricter TypeScript 2.4 checks

Here is the corresponding issue。这似乎是一个错误。

你是 运行 RxJS 5.3,所以这就是你得到错误的原因。

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

正如我在评论中提到的,您函数的 return 类型也需要更改为 Observable<boolean>

应用在你的片段上,应该是这样的:

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

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

备选

如@cartant 所述,Subject.lift 实现为 return a Subject,但不能用 TypeScript 的类型表示(没有 return 类型重载,甚至不确定是否有支持该语言的语言)。

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