2017-09-08 4 views
0

Mit typischem Typkönnen wir Generika und alle Kompiliergüte verwenden, um eine Art Typsicherheit zu gewährleisten. Aber wenn wir zum Beispiel den HTTP-Service verwenden, erhalten wir kein spezifisches Objekt, sondern nur geparsten JSON. Zum Beispiel haben wir einige generischen Methoden zu tun, dass:TypeScript: Rekursives Erstellen komplexer Objekte

public get<T>(relativeUrl: string): Promise<T> { 
    const completeUrlPromise = this.createCompleteUrl(relativeUrl); 
    const requestOptions = this.createRequestOptions(ContentType.ApplicationJson, true); 
    return completeUrlPromise.then(completeUrl => { 
     return this.processResponse<T>(this.http.get(completeUrl, requestOptions)); 
    }); 
    } 

    private processResponse<T>(response: Observable<Response>): Promise<T> { 
    const mappedResult = response.map(this.extractData); 

    const result = mappedResult.toPromise(); 
    return result; 
    } 

    private extractData(res: Response): any { 
    let body; 
    if (!Array.isArray(res)) { 
     if (res.text()) { 
     body = res.json(); 
     } 
    } else { 
     body = res; 
    } 

    if (!JsObjUtilities.isNullOrUndefined(body)) { 
     return body; 
    } 

    return {}; 
    } 

Letztlich ist die generische Art nutzlos ist auf diese Weise, da wir nur die JSON erhalten. Wenn das generische Objekt über Methoden oder Eigenschaften verfügt, die nicht in JSON enthalten sind, sind sie verloren. dies zu vermeiden, haben wir die Möglichkeit, eine Konstruktor-Funktion zu übergeben wirklich das Objekt zu erstellen:

private processResponse<T>(response: Observable<Response>, ctor: IParameterlessConstructor<T> | null = null): Promise<T> { 
    let mappedResult = response.map(this.extractData); 

    if (ctor) { 
     mappedResult = mappedResult.map(f => { 
     const newObj = JsObjFactory.create(f, ctor); 
     return newObj; 
     }); 
    } 

    const result = mappedResult.toPromise(); 
    return result; 
    } 

Und die JsObjFactory wie folgt aussehen:

export class JsObjFactory { 
    public static create<T>(source: any, ctorFn: IParameterlessConstructor<T>): T { 
    const result = new ctorFn(); 
    this.mapDefinedProperties(source, result); 

    return result; 
    } 

    private static mapDefinedProperties<T>(source: Object, target: T): void { 
    const properties = Object.getOwnPropertyNames(target); 

    properties.forEach(propKey => { 
     if (source.hasOwnProperty(propKey)) { 
     target[propKey] = source[propKey]; 
     } 
    }); 
    } 
} 

Das funktioniert gut für flache Objekte, sondern funktioniert nicht, wenn eine Eigenschaft auch ein komplexer Typ mit einem Konstruktor ist. Da es zur Laufzeit keine Typen gibt, ist die beste Wette, die ich derzeit habe, die Eigenschaften zu analysieren, zu prüfen, ob Klassen existieren und sie dann zu erstellen. Dies scheint jedoch sehr fehleranfällig und umständlich zu sein.

Da bin ich immer sicher, ich bin nicht die einzige Person mit diesen Problemen, gibt es Lösungen, oder TypeScript/JavaScript-Funktionen, die mir nicht bekannt sind, was würde hier helfen?

+0

Mögliches Duplikat von [Wie initialisiere ich ein Typescript-Objekt mit einem JSON-Objekt] (https://stackoverflow.com/questions/22885995/how-do-i-initialize-a-typescript-object-with-a- json-object) (dort sind einige gute Antworten) – jcalz

Antwort

0

Ich persönlich mache es nicht so, aber es könnte sein, was Sie suchen.

Beispiel:

Customer.ts

export interface ICustomer { 
    Id: number; 
    Name: string; 
    Orders: IOrder[]; 
    ... 
} 

export class Customer implements ICustomer { 
    public Id: number; 
    public Name: string; 
    public Orders: IOrder[]; 

    constructor(customer: Partial<ICustomer>) { 
     this.Id = customer.Id || 0; 
     this.Name = customer.Name || ''; 
     this.Orders = []; 
     customer.Orders.forEach((order: IOrder) => this.Orders.push(new Order(order))); 
    } 

    //some functions 
} 

Order.ts

export interface IOrder { 
    Id: number; 
    Weight: number; 
    Shipmentdate: string; 
} 

export class Order implements IOrder { 
    public Id: number; 
    public Weight: number; 
    public Shipmentdate: string; 

    constructor(order: Partial<IOrder>) { 
     this.Id = order.Id || 0; 
     this.Weight = order.Weight || 0; 
     this.Shipmentdate = order.Shipmentdate || ''; 
    } 

    //some functions 
} 

Dies würde die Object machen (in diesem Fall Customer) verantwortlich für die Instanziierung es bekannt ist komplexe Typen, die Sie weitergeben. Und Order wiederum könnte seine komplexen Typen haben, die es instanziiert.

+0

Danke, versuche es mal. Die Frage ist, woher weiß ich nur aus dem JSON was außer Namen zu erstellen ist? –

+0

@ MatthiasMüller Die Klassen selbst wissen? Oder missverstehe ich dich? – Arg0n

Verwandte Themen