You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何识别Observable的多种返回类型?API多返回类型处理技术问询

How to Identify Multiple Return Types of an Observable

I'm calling an API to fetch a product list. When an error occurs, the API returns a response containing a status code and error message. Here's the relevant code:

product-service.ts:

getAllProducts(): Observable<IProduct[] | ApiResponse> { 
  return this._http.get<IProduct[]>(this._GetAllUrl) 
    .do(data => console.log("All: " + JSON.stringify(data))) 
    .catch(err => this.HandleError(err)); 
}

api-response.ts:

export class ApiResponse { 
  StatusCode: number; 
  Message: string; 
}

product.ts:

export interface IProduct {
  // Product properties defined here
}

I want to know how to identify the multiple return types (IProduct[] or ApiResponse) from this Observable.


Answer

Great question! When dealing with an Observable that can emit one of multiple types, TypeScript type guards are your go-to solution. Let's break down how to handle this cleanly:

1. Create a Custom Type Guard Function

First, build a type guard to check if an object is an ApiResponse at runtime. This tells TypeScript exactly what type it's dealing with in different code paths:

function isApiResponse(obj: unknown): obj is ApiResponse {
  const response = obj as ApiResponse;
  return typeof response === 'object' && response !== null &&
         'StatusCode' in response && typeof response.StatusCode === 'number' &&
         'Message' in response && typeof response.Message === 'string';
}

2. Use the Type Guard in Your Subscription

When subscribing to the Observable, use the type guard to branch logic based on the returned type:

this.productService.getAllProducts().subscribe({
  next: (data) => {
    if (isApiResponse(data)) {
      // TypeScript now knows data is an ApiResponse
      console.error(`Request failed: ${data.StatusCode} - ${data.Message}`);
      // Handle error UI state (show alert, disable buttons, etc.)
    } else {
      // TypeScript now knows data is an IProduct[]
      console.log('Fetched products:', data);
      // Render your product list
    }
  },
  error: (err) => {
    // Catch unexpected errors that your HandleError might not cover
    console.error('Unexpected error:', err);
  }
});

3. Optional: Improve the Return Type with a Discriminated Union (Best Practice)

For even clearer type handling, wrap your success/error states in a discriminated union. This makes type checks more explicit and harder to mess up:

// Define a union type with a discriminator property
type ProductFetchResult = 
  | { type: 'success', products: IProduct[] }
  | { type: 'error', error: ApiResponse };

// Update your service method to return this union type
getAllProducts(): Observable<ProductFetchResult> { 
  return this._http.get<IProduct[]>(this._GetAllUrl) 
    .pipe(
      tap(data => console.log("All: " + JSON.stringify(data))),
      map(data => ({ type: 'success', products: data })),
      catchError(err => {
        const apiError = this.HandleError(err);
        return of({ type: 'error', error: apiError });
      })
    );
}

Now subscribing becomes even more intuitive with a switch statement:

this.productService.getAllProducts().subscribe({
  next: (result) => {
    switch(result.type) {
      case 'success':
        console.log('Products loaded:', result.products);
        // Render product list
        break;
      case 'error':
        console.error('Load failed:', result.error.StatusCode, result.error.Message);
        // Show error message to user
        break;
    }
  }
});

Quick Note on Your HandleError Method

Ensure your HandleError function correctly returns an Observable<ApiResponse> so the catch block maps errors to the expected type. Here's a quick example of how that might look:

private HandleError(error: HttpErrorResponse): Observable<ApiResponse> {
  let statusCode = error.status;
  let message = 'Unknown error occurred';

  if (error.error instanceof ErrorEvent) {
    // Client-side error
    message = `Client error: ${error.error.message}`;
  } else if (error.error && isApiResponse(error.error)) {
    // Server returned a valid ApiResponse
    return of(error.error);
  } else {
    // Fallback for non-standard server errors
    message = `Server error: ${error.message}`;
  }

  return of({ StatusCode: statusCode, Message: message });
}

内容的提问来源于stack exchange,提问作者Reed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:36:25