无法创建通用参数类的对象

时间:2019-05-16 13:20:38

标签: typescript oop

我是一位在Typescript方面苦苦挣扎的Java开发人员。 我有一个名为Shape的抽象类,以及两个继承自它的具体类:Square和Circle。我还想将构造函数隐藏在那些类中,并创建一个工厂方法getNewShape,该方法将基于一个参数来决定使用哪个具体的c'tor。在Java中,我只需写(在Shape中):

public static <T extends Shape> T getNewShape(T reference){
    // my logic
    reference = T.class.getInstance();
    return reference;
}

在Typescript中,我现在想像这样使用它:

let myShape = Square.getNewShape(myShape);

我阅读了其他可以使用的问题:

in the consumer:
let myShape = Square.getNewShape(myShape, Square);

in Shape:
public static <T extends Shape>(ref: T, type): T{
   //my logic
   reference = new type();
   return reference;
}

但是我更喜欢如果我已经从具体的类中调用getNewShape,则不需要在参数中添加Type,而打字稿只需从T推断出来即可。

更新:添加完整的代码:

    class Shape {
    constructor(){

    }

    public static getNewShape<T extends Shape>(ref: T, type: new() => T): T{
        if(ref) {
            ref.clear();
        }
        ref = new type();
        return ref;
    }

    protected clear(){
        console.log('clearing');
    }
}

class Circle extends Shape{
    protected constructor(){
        super();
    }
}

class Square extends Shape{
    protected constructor(){
        super();
    }
}

class App {
    private _circle: Circle = null;
    constructor(){

    }

    public setup(){
        this._circle = Circle.getNewShape(this._circle, Circle);
    }
}

1 个答案:

答案 0 :(得分:0)

这是前进的一种可能方法:

abstract class Shape {
  public static getNewShape<T extends Shape>(
    this: { prototype: T }, // ctors have a strongly typed prototype
    ref: T
  ): T {
    if (ref) {
      ref.clear();
    }
    // assert that this is a constructor
    ref = new ((this as any) as new () => T)();
    return ref;
  }

  protected clear() {
    console.log("clearing");
  }
}

class Circle extends Shape {
  private constructor() {
    super();
  }
  radius: number = 0; // add property
}

class Square extends Shape {
  private constructor() {
    super();
  }
  side: number = 0; // add property
}

class App {
  private _circle: Circle = null!; // yeah, how do you get this?!
  constructor() {}

  public setup() {
    this._circle = Circle.getNewShape(this._circle); // okay
    Square.getNewShape(this._circle); // error!
  }
}

相关的部分是:

  • 将构造函数标记为protectedprivate时会遇到很大的限制,并且不能同时使用两种方法。如果Circle的构造函数不是公共的,则不能在Shape.getNewShape()内部调用它,因为即使受保护的构造函数也只能从子类访问,而不能从超类访问。幸运的是,有type assertions使我们可以在Shape.getNewShape()的实现中覆盖编译器的抱怨。当然,这不是类型安全的。所以你必须要小心。

  • 不是将T的构造函数称为new()=>T,(如您所述)不适用于privateprotected构造函数,我们可以利用这样的事实,即TypeScript中的类构造函数具有强类型的prototype属性,该属性也具有T类型,无论构造函数是否为公共。因此,new()=>T结束了,{prototype: T}结束了。请注意,这并不能正确地传达出构造函数的参数为​​零的事实……再结合上面的类型断言,这意味着您需要如果Square没有参数,请确保Circlenew在运行时不会爆炸。

  • 请注意,您正在调用构造函数的静态方法,而不是将构造函数作为参数传递给SomeShape.getNewShape(),这意味着构造函数应该是该调用的this上下文。这样我们就可以new this()了。而且我们可以使用this parameter来防止用户在错误的构造函数上调用该方法(Square.getNewShape(someCircle)应该是错误的)。

那些是有效成分。我仍要提及的次要细节:

  • 我在CircleSquare中添加了一些属性,以在结构上彼此区分,以及与Shape和空类进行区分。如果要避免怪异,即使在使用示例代码的情况下,在TypeScript中也是important to do。 TypeScript中的类型系统是 structural ,而不是 nominal ;也就是说,类型是通过其形状而不是其名称识别的。如果类型/接口/类AB共享相同的属性键和值,则尽管名称不同,但编译器仍将它们视为相同的类型/接口/类。

  • 我仍然不确定如果构造函数不可访问,那么有人如何首次有效引用SquareCircle实例。对我来说,这似乎是一个引导问题。 Square.getNewShape(square)在给定现有Square的情况下给了我一个新的Square,但是现有Square从何而来?也许周围有CircleSquare的一些静态实例?或者,也许您还有另一种方法? ...或者,这只是在我身上发生,也许您认为nullShape的有效实例。您不使用--strictNullChecks--strict编译器选项吗?你真的应该然后,您可以将ref注释为T | null而不是T,这样就明确了接受null的意图。


所有这些,我真的建议不要隐藏构造函数,如果您只是要公开一个静态方法来调用它们的话。这非常复杂,我不知道它能为您带来什么。相反,可以考虑只将构造函数公开为public并在抽象getNewShape()构造函数内进行Shape中所做的所有整理工作:

abstract class Shape {
  constructor(ref: Shape | null) {
    if (ref) {
      ref.clear();
    }
  }

  protected clear() {
    console.log("clearing");
  }
}

class Circle extends Shape {
  constructor(ref: Circle | null) {
    super(ref);
  }
  radius: number = 0; // add property
}

class Square extends Shape {
  constructor(ref: Square | null) {
    super(ref);
  }
  side: number = 0; // add property
}

class App {
  private _circle: Circle | null = null;
  constructor() {}

  public setup() {
    this._circle = new Circle(this._circle); // okay
    new Square(this._circle); // error!
  }
}

在我看来,这是一样的,并且不会因为试图强制TypeScript做它不想做的事情而受苦。


好的,希望能有所帮助;祝你好运!