迁移 Angular 和 RxJS 5 到 6 后的错误 - 类型 'Observable<{}>' 不可分配给类型 'Observable<....>'
Errors after migrated Angular and RxJS 5 to 6 - Type 'Observable<{}>' is not assignable to type 'Observable<....>'
我将一个 Angular 项目从 v5 迁移到 v6。
为了更新我已经 运行 rxjs-5-to-6-migrate
:
的所有导入
npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
但现在我有如下错误:
src/app/products/product.service.ts(54,4): error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<{ count: number; next: string; previous: string; results: any[]; }>'.
Type '{}' is not assignable to type '{ count: number; next: string; previous: string; results: any[]; }'.
Property 'count' is missing in type '{}'.
product.service.ts:
import { Injectable } from '@angular/core';
//import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { catchError, map, tap, finalize } from 'rxjs/operators';
import { Product } from './product';
import { SpinnerService } from './../utilities/spinner/spinner.service';
import { environment } from '../../environments/environment';
const endpoint = environment.apiHost+'/api/products/' //'http://127.0.0.1:8000/api/products/'
@Injectable()
export class ProductService {
/* Caching few data that does not change so often */
private productTypes: any[];
private departments: any[];
private authors: any[];
private colors: any[];
private sizeRuns: any[];
constructor(private http: HttpClient, private _spinnerService: SpinnerService) { }
list(params?): Observable<{count:number, next:string, previous:string, results: any[]}> {
return this.http.get<{count:number, next:string, previous:string, results: any[]}>(endpoint, {params: params})
.pipe(
catchError(this.handleError<any>('Retrieving products'))
);
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T>(operation='Operation', result?: T) {
return (error: any): ErrorObservable | Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an Observable with empty result.
//return of(result as T);
return new ErrorObservable(error);
};
}
}
我在 Whosebug 上看到了其他类似的问题,但我仍然不明白如何解决。
我或许可以将界面 {count:number, next:string, previous:string, results: any[]}
更改为简单的 any
,但我真的不想这样做。
有什么解决办法吗?
UPDATE1:使用接口
interface PaginatedList {
count: number;
next: string;
previous: string;
results: any[];
}
@Injectable()
export class ProductService {
/* Caching few data that does not change so often */
private productTypes: any[];
private departments: any[];
private authors: any[];
private colors: any[];
private sizeRuns: any[];
constructor(private http: HttpClient, private _spinnerService: SpinnerService) { }
list(params?): Observable<PaginatedList> {
this._spinnerService.show('productListSpinner');
return this.http.get<PaginatedList>(endpoint, {params: params})
.pipe(
catchError(this.handleError<any>('Retrieving products')),
finalize(() => this._spinnerService.hide('productListSpinner'))
);
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T>(operation='Operation', result?: T) {
return (error: any): ErrorObservable | Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an Observable with empty result.
//return of(result as T);
return new ErrorObservable(error);
};
}
}
错误:
src/app/products/product.service.ts(61,4): error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<PaginatedList>'.
Type '{}' is not assignable to type 'PaginatedList'.
Property 'count' is missing in type '{}'.
更新2:
检查我的错误,我认为 ErrorObservable
导致了其他错误:
src/app/products/product.service.ts(325,26): error TS2314: Generic type 'ErrorObservable<T>' requires 1 type argument(s).
您可以定义 'or' 运算符。
Observable<{count:number, next:string, previous:string, results: any[]}> | Observable<any>
I can probably change the interface {count:number, next:string,
previous:string, results: any[]} to simply any`
这样做只会搬起石头砸自己的脚。你为什么不定义一个你知道的概念的接口?
处理错误的第一个问题:我需要优雅还是不优雅处理?
优雅地 处理错误会将其转换为流中的 "fake" 通知,在大多数情况下保持其类型合同。例如:
import { throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
// Create source Observable<string> that emits an error
const source : Observable<string> = throwError('This is an error!');
// Gracefully handle error, returning observable with error message
// Notice that the type contract of the source is mantained
const example : Observable<string> = source.pipe(catchError(val => of(`I caught: ${val}`)));
// Output: 'I caught: This is an error'
// Notice that the next, and not the error callback, is invoked
const subscribe = example.subscribe(
val => console.log(val),
error => console.log("Something exploded: ", error));
在前面的例子中,我保留了源的类型约定。以下我没有:
import { throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
// Create source Observable<string> that emits an error
const source : Observable<string> = throwError('This is an error!');
// Gracefully handle error, returning observable with error message
// Notice that by mapping the error to a fake notification of another type, the new
// stream extends the type contract of the source
const example : Observable<string | number> = source.pipe(catchError(val => of(1)));
// Output: 'I caught: This is an error'
// Notice that the next, and not the error callback, is invoked
const subscribe = example.subscribe(
val => console.log(val),
error => console.log("Something exploded: ", error));
另一种选择是 不优雅地 处理错误,基本上是应用一些逻辑然后重新抛出。在这种情况下,the type contract of the stream has no information about the error that the stream could arise。例如:
import { throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
// Create source Observable<string> that emits an error
const source : Observable<string> = throwError('This is an error!');
// Ungracefully handle error, re-throwing an object
const example : Observable<string> = source.pipe(catchError(error => throwError({message: 'Error caught', error})));
// Output: 'Something exploded: '
// Notice that the error, and not the next callback, is invoked
const subscribe = example.subscribe(
val => console.log(val),
error => console.log("Something exploded: ", error));
回到你的问题;事实上,您的方法目前正在 优雅地 处理错误并扩展源流的类型合同,而没有正确声明方法的 return 类型。
错误的解决方法是将方法的签名更改为:
list(params?): Observable<PaginatedList | any>;
还有一个问题就是你直接使用ErrorObservable
,这确实是一个实现细节。通常您会使用 throwError
运算符。
我将一个 Angular 项目从 v5 迁移到 v6。
为了更新我已经 运行 rxjs-5-to-6-migrate
:
npm install -g rxjs-tslint
rxjs-5-to-6-migrate -p src/tsconfig.app.json
但现在我有如下错误:
src/app/products/product.service.ts(54,4): error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<{ count: number; next: string; previous: string; results: any[]; }>'.
Type '{}' is not assignable to type '{ count: number; next: string; previous: string; results: any[]; }'.
Property 'count' is missing in type '{}'.
product.service.ts:
import { Injectable } from '@angular/core';
//import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { ErrorObservable } from 'rxjs/observable/ErrorObservable';
import { catchError, map, tap, finalize } from 'rxjs/operators';
import { Product } from './product';
import { SpinnerService } from './../utilities/spinner/spinner.service';
import { environment } from '../../environments/environment';
const endpoint = environment.apiHost+'/api/products/' //'http://127.0.0.1:8000/api/products/'
@Injectable()
export class ProductService {
/* Caching few data that does not change so often */
private productTypes: any[];
private departments: any[];
private authors: any[];
private colors: any[];
private sizeRuns: any[];
constructor(private http: HttpClient, private _spinnerService: SpinnerService) { }
list(params?): Observable<{count:number, next:string, previous:string, results: any[]}> {
return this.http.get<{count:number, next:string, previous:string, results: any[]}>(endpoint, {params: params})
.pipe(
catchError(this.handleError<any>('Retrieving products'))
);
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T>(operation='Operation', result?: T) {
return (error: any): ErrorObservable | Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an Observable with empty result.
//return of(result as T);
return new ErrorObservable(error);
};
}
}
我在 Whosebug 上看到了其他类似的问题,但我仍然不明白如何解决。
我或许可以将界面 {count:number, next:string, previous:string, results: any[]}
更改为简单的 any
,但我真的不想这样做。
有什么解决办法吗?
UPDATE1:使用接口
interface PaginatedList {
count: number;
next: string;
previous: string;
results: any[];
}
@Injectable()
export class ProductService {
/* Caching few data that does not change so often */
private productTypes: any[];
private departments: any[];
private authors: any[];
private colors: any[];
private sizeRuns: any[];
constructor(private http: HttpClient, private _spinnerService: SpinnerService) { }
list(params?): Observable<PaginatedList> {
this._spinnerService.show('productListSpinner');
return this.http.get<PaginatedList>(endpoint, {params: params})
.pipe(
catchError(this.handleError<any>('Retrieving products')),
finalize(() => this._spinnerService.hide('productListSpinner'))
);
}
/**
* Handle Http operation that failed.
* Let the app continue.
* @param operation - name of the operation that failed
* @param result - optional value to return as the observable result
*/
private handleError<T>(operation='Operation', result?: T) {
return (error: any): ErrorObservable | Observable<T> => {
// TODO: send the error to remote logging infrastructure
console.error(error); // log to console instead
// TODO: better job of transforming error for user consumption
console.log(`${operation} failed: ${error.message}`);
// Let the app keep running by returning an Observable with empty result.
//return of(result as T);
return new ErrorObservable(error);
};
}
}
错误:
src/app/products/product.service.ts(61,4): error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<PaginatedList>'.
Type '{}' is not assignable to type 'PaginatedList'.
Property 'count' is missing in type '{}'.
更新2:
检查我的错误,我认为 ErrorObservable
导致了其他错误:
src/app/products/product.service.ts(325,26): error TS2314: Generic type 'ErrorObservable<T>' requires 1 type argument(s).
您可以定义 'or' 运算符。
Observable<{count:number, next:string, previous:string, results: any[]}> | Observable<any>
I can probably change the interface {count:number, next:string, previous:string, results: any[]} to simply any`
这样做只会搬起石头砸自己的脚。你为什么不定义一个你知道的概念的接口?
处理错误的第一个问题:我需要优雅还是不优雅处理?
优雅地 处理错误会将其转换为流中的 "fake" 通知,在大多数情况下保持其类型合同。例如:
import { throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
// Create source Observable<string> that emits an error
const source : Observable<string> = throwError('This is an error!');
// Gracefully handle error, returning observable with error message
// Notice that the type contract of the source is mantained
const example : Observable<string> = source.pipe(catchError(val => of(`I caught: ${val}`)));
// Output: 'I caught: This is an error'
// Notice that the next, and not the error callback, is invoked
const subscribe = example.subscribe(
val => console.log(val),
error => console.log("Something exploded: ", error));
在前面的例子中,我保留了源的类型约定。以下我没有:
import { throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
// Create source Observable<string> that emits an error
const source : Observable<string> = throwError('This is an error!');
// Gracefully handle error, returning observable with error message
// Notice that by mapping the error to a fake notification of another type, the new
// stream extends the type contract of the source
const example : Observable<string | number> = source.pipe(catchError(val => of(1)));
// Output: 'I caught: This is an error'
// Notice that the next, and not the error callback, is invoked
const subscribe = example.subscribe(
val => console.log(val),
error => console.log("Something exploded: ", error));
另一种选择是 不优雅地 处理错误,基本上是应用一些逻辑然后重新抛出。在这种情况下,the type contract of the stream has no information about the error that the stream could arise。例如:
import { throwError, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
// Create source Observable<string> that emits an error
const source : Observable<string> = throwError('This is an error!');
// Ungracefully handle error, re-throwing an object
const example : Observable<string> = source.pipe(catchError(error => throwError({message: 'Error caught', error})));
// Output: 'Something exploded: '
// Notice that the error, and not the next callback, is invoked
const subscribe = example.subscribe(
val => console.log(val),
error => console.log("Something exploded: ", error));
回到你的问题;事实上,您的方法目前正在 优雅地 处理错误并扩展源流的类型合同,而没有正确声明方法的 return 类型。
错误的解决方法是将方法的签名更改为:
list(params?): Observable<PaginatedList | any>;
还有一个问题就是你直接使用ErrorObservable
,这确实是一个实现细节。通常您会使用 throwError
运算符。