typescript - Angular-tjänst - Typen på det objekt som returneras är object och inte den generiska typ som specificerats
Jag har en Angular5-tjänst som gör en HTTP GET och returnerar en specifik typ som visas nedan
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)
);
}
Produktklassen är en klass som har egenskaper och metoder. Ett litet utdrag är nedan.
class Product{
productID:string;
productName:string;
setQuantity(quantity:number){
}
}
När jag anropar setQuantity-funktionen på this._product som returneras av get får jag ett felmeddelande 'setQuantity är inte en funktion'. När jag försöker kontrollera instansen av this._product är den inte av typen Product utan av typen Object.
Är den generiska typen på get-metoden endast till för att hjälpa till med typkontroll vid kompilering? Hur får jag en konkret instans av produktklassen från metoden getProductByIDproductId?
Svar #1
Du kan inte göra det du gör.
När du hämtar data från en URL får du bara JSON. Du säger till TypeScript att data är av typen Product, men det är bara ett tips för kompilatorn och gör det inte sant.
data skapades inte med ett anrop till new Product och delar därför inte dess metoder.
Om du vill att din this._product ska bete sig som en inbyggd Product-instans, kan du göra så här:
this._product = data;
Object.setPrototypeOf(this._product, Product.prototype);
På det här sättet gör du om this._product till en verklig Product-instans, inklusive dess metoder.
Ett annat alternativ, om du är orolig för setPrototypeOf och dess potentiella prestandanackdel, är att göra det på det här sättet:
this._product = new Product();
Object.assign(this._product, data);
Självklart är detta bara en bra idé om Product har en parameterlös konstruktor. Och, i vilket fall som helst, om data har egenskaper som inte är definierade i Product-klassen kan du också stöta på prestandaproblem.
Svar #2
Du kunde till exempel kartlägga svaret.
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)
);
På det här sättet är du säker på att ditt objekt kommer att vara ett Typescript-objekt som du definierade.
REDIGERA Du kan skapa ett objekt "för hand" med bara några rader :
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;
}
}
I din kartläggning :
.map(response => Product.createProductFromHttpResponse(response))
