TypeScript:递归创建复杂对象

时间:2017-09-08 11:40:45

标签: javascript json typescript generics

使用Angular& TypeScript,我们可以使用泛型和所有Compile-goodness来确保某种类型的安全性。但是,如果我们使用例如HTTP服务,我们不会获得特定的目标,只是解析了JSON。例如,我们有一些通用的方法:

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 {};
  }

最终,泛型类型以这种方式无用,因为我们只获得了JSON。如果通用对象具有不在JSON中的方法或属性,则它们将丢失。 为了避免这种情况,我们增加了传递构造函数来真正创建对象的可能性:

 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;
  }

JsObjFactory看起来像这样:

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];
      }
    });
  }
}

这适用于浅层对象,但如果属性也是带构造函数的复杂类型,则不起作用。由于在运行时没有类型,我目前最好的办法是善意解析属性,检查类是否存在然后创建它们。但这似乎非常容易出错并且很麻烦。

由于我始终确定,我不是唯一有这个问题的人,是否有解决方案,或者我不知道的TypeScript / JavaScript功能,这会有帮助?

1 个答案:

答案 0 :(得分:0)

我个人不是这样做的,但它可能是你正在寻找的。

示例:

<强> 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
}

这会使Object(在这种情况下为Customer)负责实例化您传入的已知复杂类型。Order反过来又可能有其复杂性它实例化的类型。