Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Example code, using Angular 4.1.3, rxjs 5.3.0, typescript 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();
  }
}

When I try to compile this code, typescript complains about the last line:

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

My sense is that this is saying that first() is a method of Observable, not Subject, but my understanding is that a Subject is also an Observable, so this should not be a problem. And, in fact, if I ignore the error the code compiles and runs fine.

Things I have tried:

  1. Various ways of importing the first operator explicitly, e.g. import 'rxjs/add/operator/first'. This does not change the error.
  2. Casting the Subject to an Observable before calling first(), e.g. return (this.response$ as Observable<boolean>).first();. This results in a different but similar error, which also seems incorrect to me: Type 'Observable<boolean>' is not assignable to type 'Subject<boolean>'. Property 'observers' is missing in type 'Observable<boolean>'.

How can I convince typescript that it is valid to call first() on a Subject?

To clarify in response to the discussion of return types below: the method may also have had an incorrect return type (should be Observable rather than Subject) but that appears to be a separate issue; changing the return type does not resolve the error described above. A further detail confirming this is that when vscode highlights the error, it only highlights this.response$, not the entire return line, suggesting that the problem is between this.response$ and first(), not between first() and the function's signature:

error highlighted in vscode

like image 794
mactyr Avatar asked Sep 17 '18 17:09

mactyr


1 Answers

Your understanding that a Subject is also an Observable is correct but to an extent that a Subject can also be used as an Observable. But that doesn't make a Subject an Observable and vice versa.

As suggested by ggradnig you should be changing the return type of your confirm function to Observable<boolean> instead of Subject<boolean> since the first method on the Subject<boolean> would return an 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();
  }
}
like image 161
SiddAjmera Avatar answered Oct 11 '22 07:10

SiddAjmera