TypeScript Factory子类不可分配给类型超类

时间:2017-11-13 04:52:41

标签: typescript generics reflection factory

我想使用TypeScript将Factory Pattern用于将原始数据转换为User。我有两种不同类型的UserEmployeeCustomer。两者都扩展基类并编译没有问题。以下是这三个类的极简化版本。

abstract class User {
  constructor(data: any) { }

  static createFromServer<T extends User>(this: new (...args: any[]) => T, data: any): T {
    return new this(data);
  }
}

class Employee extends User {
  static '@type' = 'Employee';

  static createFromServer<T extends User>(this: new (...args: any[]) => T, data: any): T {
    return new this(data);
  }
}

class Customer extends User {
  static '@type' = 'Customer';

  static createFromServer<T extends User>(this: new (...args: any[]) => T, data: any): T {
    return new this(data);
  }
}

足够简单。现在为工厂。我想创建一个Factory,它将从服务器返回原始数据,并将该数据转换为系统其余部分可以使用的内容(即User的实例)。我想要做的事情就像是

class UserFactory {
  /**
   * Vends a User constructed from the raw data from the server.
   *
   * @param userData (Object) - the User definition, as obtained from the server
   *
   * @return (User) the User initialized from `userData`
   */
  static vend<T extends User>(userData: any): T {
    switch (userData['@type']) {
      case Employee['@type']:
        return Employee.createFromServer(userData);

      case Customer['@type']:
        return Customer.createFromServer(userData);

      default:
        throw new Error('Unknown User type.');
    }
  }
}

正如你可能猜到的那样(因为我在这里,而不是在周末放松),这不起作用。我收到以下错误:

  

键入&#39;员工&#39;不能分配给&#39; T&#39;。

由于T扩展UserEmployee扩展User而未添加或移除任何内容(在此简化版本中),它似乎是上面的应该工作。更令人烦恼的是,上面的内容似乎可以用于其他语言(例如Java(测试版),C ++),我不知道为什么它在这种情况下无法工作。

GitHub上有人建议问题可能在于overloading the constructor,但显然这里没有发生。

那么,为什么不能将Employee分配给T类型? (怎么样)我可以让上面的工作吗?有没有更好的方法在TypeScript中处理这个?正确的答案至少会回答前两个问题。

附录

出于某种原因,在子类中重新定义了

createFromServer()。这是一个可重复性最低的例子。构造函数实际上并不从服务器获取原始数据。相反,createFromServer()将原始数据转换为构造函数可以理解的参数。

2 个答案:

答案 0 :(得分:0)

我无法说明TypeScript无法正确推断类型的具体原因,但是,转换为any会允许代码编译和类型推断正常工作。例如

class UserFactory {
    /**
     * Vends a User constructed from the raw data from the server.
     *
     * @param userData (Object) - the User definition, as obtained from the server
     *
     * @return (User) the User initialized from `userData`
     */
    static vend<T extends User>(userData: any) : T {
      switch (userData['@type']) {
        case Employee['@type']:
          return <any>Employee.createFromServer(userData); // Cast

        case Customer['@type']:
          return <any>Customer.createFromServer(userData); // Cast

        default:
          throw new Error('Unknown User type.');
      }
    }
  }

// Tests
const employee = UserFactory.vend<Employee>("");
employee.customerId = 7; // Error
employee.employeeId = 7;

const customer = UserFactory.vend<Customer>("");
customer.customerId = 7;
customer.employeeId = 7; // Error


// Added id properties to Employee and Customer
  abstract class User {
    constructor(data: any) { }

    static createFromServer<T extends User>(this: new (...args: any[]) => T, data: any): T {
      return new this(data);
    }
  }

  class Employee extends User {
    static '@type' = 'Employee';
    public employeeId : number;

    static createFromServer<T extends User>(this: new (...args: any[]) => T, data: any): T {
      return new this(data);
    }
  }

  class Customer extends User {
    static '@type' = 'Customer';
    public customerId : number;
    static createFromServer<T extends User>(this: new (...args: any[]) => T, data: any): T {
      return new this(data);
    }
  }

答案 1 :(得分:-1)

It's not working because you've written too much code. You should be leveraging polymorphism. What this means is simply removing the static factory methods from the two base classes.

That way will work as you expect and you will have less code to maintain. In fact, it's the very reason why the static method in the User class has a this type and news the containing class using the this reference. It enables derived classes to inherit a factory that intrinsically has their more derived type

Also this isn't Java. In fact I couldn't think of a language less like Java than TypeScript