Eine alte Frage mit meist richtigen, aber nicht sehr effizienten Antworten. Das was ich vorschlage:
Erstellen Sie eine Basisklasse, die die Methode init () und statische Umwandlungsmethoden enthält (für ein einzelnes Objekt und ein Array). Die statischen Methoden können überall sein; Die Version mit der Basisklasse und init () ermöglicht anschließend einfache Erweiterungen.
export class ContentItem {
// parameters: doc - plain JS object, proto - class we want to cast to (subclass of ContentItem)
static castAs<T extends ContentItem>(doc: T, proto: typeof ContentItem): T {
// if we already have the correct class skip the cast
if (doc instanceof proto) { return doc; }
// create a new object (create), and copy over all properties (assign)
const d: T = Object.create(proto.prototype);
Object.assign(d, doc);
// reason to extend the base class - we want to be able to call init() after cast
d.init();
return d;
}
// another method casts an array
static castAllAs<T extends ContentItem>(docs: T[], proto: typeof ContentItem): T[] {
return docs.map(d => ContentItem.castAs(d, proto));
}
init() { }
}
Ähnliche Mechaniken (mit assign () ) wurden in @ Adam111p post erwähnt. Nur eine andere (vollständigere) Möglichkeit, dies zu tun. @ Timothy Perez kritisiert zuweisen () , aber imho ist es hier völlig angemessen.
Implementieren Sie eine abgeleitete (die reale) Klasse:
import { ContentItem } from './content-item';
export class SubjectArea extends ContentItem {
id: number;
title: string;
areas: SubjectArea[]; // contains embedded objects
depth: number;
// method will be unavailable unless we use cast
lead(): string {
return '. '.repeat(this.depth);
}
// in case we have embedded objects, call cast on them here
init() {
if (this.areas) {
this.areas = ContentItem.castAllAs(this.areas, SubjectArea);
}
}
}
Jetzt können wir ein aus dem Dienst abgerufenes Objekt umwandeln:
const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);
Alle Hierarchien von SubjectArea Objekten haben die richtige Klasse.
Ein Anwendungsfall / Beispiel; Erstellen Sie einen Angular-Service (wieder abstrakte Basisklasse):
export abstract class BaseService<T extends ContentItem> {
BASE_URL = 'http://host:port/';
protected abstract http: Http;
abstract path: string;
abstract subClass: typeof ContentItem;
cast(source: T): T {
return ContentItem.castAs(source, this.subClass);
}
castAll(source: T[]): T[] {
return ContentItem.castAllAs(source, this.subClass);
}
constructor() { }
get(): Promise<T[]> {
const value = this.http.get(`${this.BASE_URL}${this.path}`)
.toPromise()
.then(response => {
const items: T[] = this.castAll(response.json());
return items;
});
return value;
}
}
Die Verwendung wird sehr einfach; Erstellen Sie einen Area-Service:
@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
path = 'area';
subClass = SubjectArea;
constructor(protected http: Http) { super(); }
}
Die Methode get () des Dienstes gibt ein Versprechen eines Arrays zurück, das bereits als SubjectArea umgewandelt wurde Objekte umgewandelt wurde (gesamte Hierarchie).
Sagen wir jetzt, wir haben eine andere Klasse:
export class OtherItem extends ContentItem {...}
Das Erstellen eines Dienstes, der Daten abruft und in die richtige Klasse umwandelt, ist so einfach wie:
@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
path = 'other';
subClass = OtherItem;
constructor(protected http: Http) { super(); }
}