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?
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.
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;
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With