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

时间:2018-09-17 17:17:29

标签: angular typescript rxjs rxjs5

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

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

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()之前将主体投射为可观察对象,例如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()是有效的?

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

error highlighted in vscode

2 个答案:

答案 0 :(得分:3)

您对主体也是可观察对象的理解是正确的,但是在一定程度上,主题也可以用作可观察对象。但这并不能使Subject变成Observable,反之亦然。

如ggradnig所建议,您应该将confirm函数的返回类型更改为Observable<boolean>而不是Subject<boolean>,因为first上的Subject<boolean>方法会返回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();
  }
}

答案 1 :(得分:1)

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

说明:

讨论之后,我研究了RxJS 5.3.0源代码,发现liftSubject之间的Observable签名不同。具体来说,我们可以看到以下区别:

Subject.ts:

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

Observable.ts:

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

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

first()运算符的Observable上下文需要一个this,由于Subject的签名不匹配,因此lift无法匹配。这对我来说很奇怪,因为Subject扩展了Observable,因此,其所有属性都应具有匹配的签名。

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

  

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

Here is the corresponding issue。看来是个错误。

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

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

如我的评论中所述,函数的返回类型也需要更改为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的实现是为了返回Subject,但这不能用TypeScript的类型表示(没有返回类型重载,甚至不能确定是否有一种语言可以支持)。

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