Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type 'Observable<Response | Observable<Response>>' is not assignable to type 'Observable<Response>'

I have a simple service with the following content:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AddressService {

  constructor(private http: Http) { }

  getAnything = (): Observable<Response> => {
    return this.http.get('https://my_api.com')
      .map(this.handleSuccess)
      .catch(this.handleError);
  }

  handleError = (error: Response): Observable<Response> => {
    return Observable.throw(error || 'Server Error');
  }

  handleSuccess = (response: Response): Observable<Response> => {
    let body;

    if (response.text()) {
      body = response.json();
    }

    return body || {};
  }
}

It was working perfectly, until I upgrade Typescript from 2.3.4 to 2.4.1.

Now, after upgrade, I'm getting the weird error:

Type 'Observable<Response | Observable<Response>>' is not assignable to type 'Observable<Response>'

What's the point here? What are the changes in TS 2.4.x that make my app stop working properly?

like image 480
dev_054 Avatar asked Jul 13 '17 23:07

dev_054


2 Answers

TypeScript 2.4 introduced better checking for generics. It is highlighting errors in your code that should be fixed.

For example, the return type of handleSuccess does not match what it is returning; it's returning an anonymous object, but is typed as returning Observable<Response>. And because it's being used with map, you end up with a composed observable that's typed as Observable<Response | Observable<Response>>.

The errors you are seeing are real and should be fixed.

like image 172
cartant Avatar answered Oct 27 '22 15:10

cartant


The change is stronger type-checking.

An Observable<Response> is not assignable to something typed as Response because they are different types. For brevity lets call these types X and Y

Observable<X | Y> is not assignable to something typed as Y because while it might contain a Y it might also contain an X, and this is rejected by the stricter checking introduced in version 2.4.1

Capture the value of your expression into a let variable and return that variable. Then you will be able to inspect the type of the variable which will show you that it's not compatible just as the compiler reports.

To make your code compile you should test for an instance of the correct type before casting.

If you don't have time to clean up your codebase you can dial back the stronger type-checking with --noStrictGenericChecks

Here's some real TS code with a problem parallel to yours. In this example, FieldInstance lacks a Form property but FormInstance has one and this allows me to distinguish them and handle the type ambiguity. FieldInstanceRepeater is assignable to FieldInstance because it is a descendant.

constructor(scope: FormInstance | FieldInstanceRepeater, field: IFormFieldRow) {
  this.FormInstance = ((function getFormInstance(fi: FormInstance | FieldInstance): FormInstance | FieldInstance {
    let isFormInstance = !!(fi as FormInstance).Form;
    return isFormInstance ? fi : getFormInstance((fi as FieldInstance).Scope);
  })(scope) as FormInstance);
  this.Scope = scope;
  ...
}
like image 2
Peter Wone Avatar answered Oct 27 '22 13:10

Peter Wone