Невозможно создать объект класса универсального параметра - PullRequest
0 голосов
/ 16 мая 2019

Я Java-разработчик, который борется с Typescript.У меня есть абстрактный класс Shape и два конкретных класса, которые его наследуют: Square и Circle.Я также хотел бы скрыть конструктор для этих классов и создать фабричный метод getNewShape, который на основе аргумента решит, какой конкретный c'tor использовать.В Java я бы просто написал (в форме):

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 голосов
/ 19 мая 2019

Вот один из возможных путей продвижения вперед:

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

Соответствующими частями этого являются:

  • Ограничения, которые возникают, когда конструктор помечен как protectedили private довольно сильны, и вы не можете иметь это в обоих направлениях.Если конструктор Circle не является общедоступным, вы не можете вызвать его внутри Shape.getNewShape(), потому что даже защищенные конструкторы доступны только из подклассов , но не суперклассов.К счастью, есть утверждения типа , которые позволяют нам переопределить жалобы компилятора внутри реализации Shape.getNewShape().Конечно, это небезопасно.Поэтому вы должны быть осторожны.

  • Вместо того, чтобы ссылаться на конструктор T как new()=>T, который (как вы заметили) не работает для вещей с privateили конструкторы protected, мы можем вместо этого использовать тот факт, что конструкторы классов в TypeScript имеют строго типизированные свойства prototype, которые также имеют тип T, независимо от того, является ли конструктор общедоступным.То есть, new()=>T отсутствует, а {prototype: T} включено. Обратите внимание, что это неправильно передает тот факт, что конструкторы будут иметь нулевые аргументы ... в сочетании с приведенным выше утверждением типа, это означает, что вам нужно убедиться, что Square и Circle не взрываются во время выполнения, если вы new их без аргументов.

  • Вместо передачи конструктора в качестве параметра SomeShape.getNewShape(), обратите внимание, чтовы вызываете статический метод конструктора, то есть конструктор должен быть контекстом this для этого вызова.Таким образом, мы можем сделать new this().И мы можем использовать this параметр , чтобы запретить пользователям вызывать метод в неправильном конструкторе (Square.getNewShape(someCircle) должно быть ошибкой).

Этоактивные ингредиенты.Незначительные детали, которые я все еще хочу упомянуть:

  • Я добавил некоторые свойства к Circle и Square, чтобы структурно отличать их друг от друга и от Shape и от пустого класса. важно сделать в TypeScript, даже с примером кода, если вы хотите избежать странностей.Система типов в TypeScript имеет вид структурный , а не номинальный ;то есть типы распознаются по их формам , а не по именам .Если type / interface / class A и B используют одни и те же ключи и значения свойств, компилятор обрабатывает их как один и тот же тип / interface / class, несмотря на разные имена.

  • Я до сих пор не уверен, как кто-то получает свою первую действительную ссылку на экземпляр Square или Circle, если конструктор недоступен.Мне кажется, это проблема с самозагрузкой;Square.getNewShape(square) дает мне новый Square с учетом существующего Square, но откуда взялся существующий Square?Может быть, есть какие-то статичные экземпляры Circle и Square, сидящие без дела?Или, может быть, у вас есть другой способ сделать это?... Или, и это только происходит со мной, может быть, вы думаете, что null является действительным экземпляром Shape.Вы не используете опции компилятора --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 делать то, что он не хочет делать.удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...