RxJS 6中如何使用catchError并返回类型化Observable?
这个问题我之前迁移Angular时也碰到过,核心是RxJS 6配合TypeScript更严格的类型检查,导致错误处理函数的返回类型和源Observable类型不兼容。
先说说为什么会报错:你原来的parseObservableResponseError返回Observable<any>,而getAlbums声明要返回Observable<Album[]>。RxJS 6的catchError会把源Observable的类型和错误处理函数返回的Observable类型做联合推断,就得到了Observable<Album[] | {}>,自然和你声明的返回类型不匹配。
要模拟RxJS 5 .catch()的行为(捕获错误、包装后重新抛出,让subscribe的错误回调处理),有两个稳妥的解决办法:
1. 让错误处理函数返回 Observable<never>
因为抛出错误的Observable永远不会发出正常的有效值,never类型是所有类型的子类型,和任何Observable的返回类型都能兼容。同时要注意RxJS 6已经废弃了Observable.throw,改用throwError操作符,推荐传递工厂函数来实现惰性求值:
parseObservableResponseError(response): Observable<never> { let err = new ErrorInfo(); // ... 你的错误解析逻辑 return throwError(() => err); // 用工厂函数代替直接传值 }
这样修改后,TypeScript会识别到错误流不会产生正常的Album[]值,就不会出现类型不匹配的问题。
2. 显式指定catchError的泛型参数
如果你不想修改错误处理函数的返回类型,可以在catchError里显式指定泛型,告诉TypeScript错误处理后的Observable类型和源Observable一致:
getAlbums(): Observable<Album[]> { return this.httpClient.get<Album[]>(this.config.urls.url("albums")) .pipe( map(albumList => this.albumList = albumList), // 显式指定输入和输出类型都是Album[] catchError<Album[], Album[]>(new ErrorInfo().parseObservableResponseError) ); }
至于你后来代码突然正常的原因,大概率是TypeScript的类型推断在某些场景下(比如throwError的返回类型被正确识别)自动兼容了,但显式处理类型会让代码更健壮,避免后续版本更新或者代码重构时再出现类似问题。
其实RxJS 6的catchError核心行为和RxJS 5的.catch()是完全一致的,都是捕获错误后返回一个新Observable来控制流的走向,只是TypeScript的类型检查更严格了而已。
内容的提问来源于stack exchange,提问作者Rick Strahl

