【问题标题】:Angular Service - Type of object returned is object and not that of the generic type specifiedAngular Service - 返回的对象类型是对象,而不是指定的泛型类型
【发布时间】:2018-03-16 10:53:52
【问题描述】:

我有一个 angular5 服务,它执行 HTTP 获取并返回如下所示的特定类型

public getProductByIDproductId: string): Observable<Product> {
    const headers = new HttpHeaders({ "Content-Type": "application/json" });
    const url = `${environment.productservice_baseurl}/${productId}`;
    return this.http
      .get<Product>(url, { headers: headers })
      .pipe(       
        tap(data => (this._product = data)),
        catchError(this.handleError)
      );
  }

Product 类是具有属性和方法的类。下面是一小段摘录。

class Product{
    productID:string;
    productName:string;

    setQuantity(quantity:number){

    }
}

当我在 get 返回的 this._product 上调用 setQuantity 函数时,我收到“setQuantity 不是函数”错误。当我尝试检查 this._product 的实例时,它不是 Product 类型,而是 Object 类型。

get 方法上设置的泛型类型是否仅用于帮助编译时类型检查?如何从 getProductByIDproductId 方法获取产品类的具体实例?

【问题讨论】:

  • 你可以使用反序列化器,比如kaiu-lab.github.io/serializer它处理继承和其他事情
  • 谢谢。是的,我需要使用序列化库。我也在看github.com/weichx/cerialize
  • Yes Cerialize 完成这项工作,除非您需要在模型中继承
  • 请您帮忙了解更多。 cerialize 不支持继承类型吗?
  • 使用 cerialize,您必须明确预期的类型。它无法处理对象是 Animal 的情况,并且基于属性它知道它是 Cat 还是 Dog。你必须明确要求狗或猫,所以...

标签: angular typescript angular5 rxjs5


【解决方案1】:

你不能做你正在做的事情。

当您从 URL 获取数据时,您只会得到 JSON。你告诉 TypeScript data 的类型是 Product,但这只是对编译器的提示,并不能使它成为真的。

data 不是通过调用new Product 创建的,因此它不共享其方法。

如果您希望您的 this._product 表现得像本机 Product 实例,您可以这样做:

this._product = data;
Object.setPrototypeOf(this._product, Product.prototype);

这样你就可以将this._product 变成一个真正的Product 实例,包括它的方法。

如果您担心setPrototypeOf 及其潜在的性能缺陷,另一种选择是这样做:

this._product = new Product();
Object.assign(this._product, data);

当然,如果Product 有一个无参数构造函数,这只是一个好主意。而且,无论如何,如果data 具有未在Product 类中定义的属性,您也可能会遇到性能问题。

【讨论】:

  • Oscar,是的,这行得通,但 MDN 警告不要使用 setPrototypeOf .. 看到这个 .. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…。我计划创建更多服务并实现所有 HTTP 动词。这会导致性能问题吗?
  • 好吧,正常情况下不应该。我的意思是,我自己做过这个。如果您真的需要 data 对象作为产品,这是最简单的方法。但是,我正在编辑我的帖子,因为有第二种方法可以做到这一点
  • 奥斯卡,谢谢。我接受你的回答。但是,我仍然不太确定如何以角度进行序列化和反序列化。这代表了我的知识差距:-)
  • 这两个提案中的任何一个都不起作用,至少在 Chrome 中。只是作为服务返回的接口。
【解决方案2】:

例如,您可以映射响应。

return this.http
  .get<Product>(url, { headers: headers })
  .map(response => new Product(response)) // I don't know your constructor
  .pipe(       
    tap(data => (this._product = data)),
    catchError(this.handleError)
  );

这样,您可以确定您的对象将是您定义的 Typescript 对象。

编辑您可以只用几行“手动”创建一个对象:

export class Product {
  // Your class ...

  public static createProductFromHttpResponse = (response: any) => {
    const ret = new Product();
    for (let key in response.key) { ret[key] = response[key]; }
    return ret;
  }
}

在您的映射中:

  .map(response => Product.createProductFromHttpResponse(response))

【讨论】:

  • 所以,本质上,我将不得不手动构造我的对象并在构造函数中对其进行初始化。我的对象很复杂,具有多个属性 (16 ),其中大多数是数组。多想这个,基本上就是序列化和反序列化。虽然你的答案会起作用,但有没有更优雅、更简单的方法来做到这一点?
  • 我进行了编辑,向您展示了一种从 HTTP 响应创建对象的快速方法。而且我想不出任何其他方法来做到这一点,因为我从未遇到过这个问题(我通常不会在我的对象中创建函数)
【解决方案3】:

据我所知,要指定响应类型,您应该使用接口而不是类,因此您的产品应该是接口:

interface Product{
productID:string;
productName:string;

}

因为它只是接口和http响应,它不应该有任何方法

编辑

@trichetriche 的回答可能是解决此问题的方法之一。

【讨论】:

    猜你喜欢
    • 2011-02-02
    • 2013-12-04
    • 2017-10-06
    • 2020-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多