Reputation: 308
I have an angular5 service which does an HTTP get and returns a specific type as shown below
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)
);
}
The Product class is a class which has properties and methods. A small excerpt is below.
class Product{
productID:string;
productName:string;
setQuantity(quantity:number){
}
}
When I call the setQuantity function on this._product returned by the get I get a 'setQuantity is not a function' error. When I try to check the instance of this._product, it is not of type Product but if type Object.
Is the generic type set on the get method only to help compile-time type checking? How do I get a concrete instance of the product class from getProductByIDproductId method?
Upvotes: 3
Views: 5325
Reputation: 18312
You can't do what you're doing.
When you fetch data from an URL, you get just JSON. You're telling TypeScript that data
is of type Product
, but that is just a hint for the compiler and does not make it true.
data
was not created with a call to new Product
and so it doesn't share its methods.
If you want your this._product
to behave like a native Product
instance, you can do this:
this._product = data;
Object.setPrototypeOf(this._product, Product.prototype);
This way you turn this._product
into a real Product
instance, including its methods.
Another option, if you are worried about setPrototypeOf
and its potential performance drawback, is doing it this way:
this._product = new Product();
Object.assign(this._product, data);
Of course, this is only a good idea if Product
has a parameterless constructor. And, in any case, if data
has properties not defined in Product
class you can also run into performance issues.
Upvotes: 5
Reputation:
You could map the response for instance.
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)
);
This way, you're sure that your object will be a Typescript object that you defined.
EDIT You can create an object "by hand" with only a few lines :
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;
}
}
In your mapping :
.map(response => Product.createProductFromHttpResponse(response))
Upvotes: 2
Reputation: 1723
As I know, to specify response type you should use interfaces instead classes, so your product should be an interface:
interface Product{
productID:string;
productName:string;
}
And as it is just interface and http response it should not have any methods
EDIT
The answer from @trichetriche probably one of the solution for this issue.
Upvotes: 0