RxJS version: [email protected]
Code to reproduce: none
Expected behavior: Observable<T,E> should be used for definition where E tell type of error to be expected
Actual behavior: Observable<T> where error is typed as any
Additional information: Typescript 2.3 provide default type for generics using that we can have Observable<T,E=any> for backward compability
I disagree. TypeScript does not have error types.
In Java, thrown error types are part of the interface contract, declared with throws. This does not exist in TypeScript, only the return type is part of the interface contract. The error type could be anything if you call other functions, so you should do runtime type checking, like checking for an error code.
Consequently, Promises don't have an error type, and Observables shouldn't either.
You could use Observable.throw to help type errors. Promises have the same problem, thats why I created Task
Here is a use case I often run into:
put<T>(path: string, body = {}): Observable<T> {
return this.http
.put<T>(`${environment.apiUrl}${path}`, JSON.stringify(body), {
headers: this.setHeaders(),
}).pipe(
catchError((err: HttpErrorResponse) => throwError(new DomainError(err.error.message)))
);
}
In this example code the intention is that I want to translate the HTTP error to my specific domain error.
The caller of the put wrapper can therefore expect either a value of type T or an error of type DomainError. However the error is typed as never/any.
Also note that I had to type the error as HttpErrorResponse otherwise the "HttpClient" from Angular would type it as any even though from the documentation I imagine that the intention was a typed error but they had no way to type it.
Was about to make an issue, but this seems close enough...
It'd be great if throwError accepted a type argument. At present, the first argument of throwError is any. (Gitter discussion).
Our use case is simple. A trading platform where an order can either _succeed_ or _fail_. If it fails, we want to terminate the stream, update the UI etc.
Consider this example:
// order.service.ts
submitOrder (order) { // implicitly Observable<Order>
return this.http.post<Order>(order).pipe(
// switch to stream of order events
switchMap(orderId => this.orderEvents$.pipe(filterById(orderId)),
// throw error if order fails
switchMap(order => order.status === 'error' ? throwError(order) : of(order))
);
}
// some-component.ts
this.service.submitOrder(order).subscribe(
successfulOrder => { ... }, // implictly type as `Order`
failedOrder => { ... } // typed as `any`
);
Were throwError to accept type arg:
switchMap(order => order.status === 'error' ? throwError<Order>(order) : of(order))
...we would have implicit typing. Without the need to coerce the type in each case of submitOrders usage.
@kyranjamie Strongly typing throwError still wouldn't allow the error in the subscribe callback to be typed differently. See Felix's comment above. If the type declarations for the error callback in subscribe are to be changed at all, they should be changed to unknown - when that top level type becomes available - because that's what they are.
Btw, the equivalent issue in TypeScript is https://github.com/Microsoft/TypeScript/issues/13219. If we ever get that issue (declarations for exceptions a function can throw), Promise would be changed to have an error type, and then Observable should be changed too. Until then, rxjs should stay consistent with how TypeScript treats synchronous exceptions and Promise rejections (including, the error type should only become unknown if promise.catch() changes any to unknown).
@felixfbecker This is an interesting point, however the developer can assert the type of observable errors and promise rejections, yet cannot specify these assertions as type parameters, while TypeScript could enforce that type contract in subscribers. TS cannot enforce that type on the provider side, but do two wrongs make it right?
I understand that the final error is a union of all possible errors in the chain (whether it's JS stack, Promise chain, Observable operator chain), so probably every then and every operator could convert error type to any, but being able to assert the error type in a final constructed Observable would be absolutely great and it doesn't require syntax additions to TS to specify possible function throws.
Most helpful comment
Btw, the equivalent issue in TypeScript is https://github.com/Microsoft/TypeScript/issues/13219. If we ever get that issue (declarations for exceptions a function can throw),
Promisewould be changed to have an error type, and thenObservableshould be changed too. Until then, rxjs should stay consistent with how TypeScript treats synchronous exceptions andPromiserejections (including, the error type should only becomeunknownifpromise.catch()changesanytounknown).