Une vieille question avec des réponses généralement correctes mais pas très efficaces. Voici ce que je propose:
Créez une classe de base qui contient la méthode init () et les méthodes de transtypage statiques (pour un seul objet et un tableau). Les méthodes statiques peuvent être n'importe où; la version avec la classe de base et init () permet ensuite des extensions faciles.
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() { }
}
Des mécanismes similaires (avec assign () ) ont été mentionnés dans @ Adam111p post. Juste une autre façon (plus complète) de le faire. @Timothy Perez critique critique assign () , mais à mon humble avis, il est tout à fait approprié ici.
Implémentez une classe dérivée (la vraie):
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);
}
}
}
Maintenant, nous pouvons lancer un objet récupéré du service:
const area = ContentItem.castAs<SubjectArea>(docFromREST, SubjectArea);
Toutes les hiérarchies d' objets SubjectArea auront la classe correcte.
Un cas d'utilisation / exemple; créer un service angulaire (classe de base abstraite à nouveau):
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;
}
}
L'utilisation devient très simple; créer un service Area:
@Injectable()
export class SubjectAreaService extends BaseService<SubjectArea> {
path = 'area';
subClass = SubjectArea;
constructor(protected http: Http) { super(); }
}
La méthode get () du service renverra une promesse d'un tableau déjà converti en objets SubjectArea (hiérarchie entière)
Maintenant, disons, nous avons une autre classe:
export class OtherItem extends ContentItem {...}
La création d'un service qui récupère les données et les convertit dans la classe correcte est aussi simple que:
@Injectable()
export class OtherItemService extends BaseService<OtherItem> {
path = 'other';
subClass = OtherItem;
constructor(protected http: Http) { super(); }
}