Как заставить функцию в абстрактном классе возвращать экземпляры дочерних классов в машинописи - PullRequest
0 голосов
/ 05 ноября 2019

Возможно ли иметь что-то вроде этого псевдокода:

abstract class AbstractParent {
    protected value: number;
    constructor(value: number) {
        this.value = value;
    }
    ...
    public times(multiplicator: number) {
        return new ???(value * multiplicator);
    }
}

, который возвращает новый Foo для class Foo extends AbstractParent и новый Bar для class Bar extends AbstractParent?

Ответы [ 2 ]

1 голос
/ 05 ноября 2019

Сопровождаемый способ - сохранить расчет times в базовом классе и реализовать отдельные фабричные методы create внутри Foo и Bar. Таким образом, базовый класс не знает о своих расширяющихся дочерних классах.

Вы объявляете create как abstract в AbstractParent, чтобы убедиться, что он будет реализован во всех подклассах. Полиморфный возвращаемый тип гарантирует для вызывающего times, что возвращаемый экземпляр имеет тот же тип подкласса ( Playground ).

abstract class AbstractParent {
    protected value: number;

    constructor(value: number) {
        this.value = value;
    }

    times(multiplicator: number) {
        return this.create(this.value * multiplicator);
    }

    // factory method creates instances of same polymorphic type (Foo / Bar)
    protected abstract create(value: number): this
}

class Foo extends AbstractParent {

    create(value: number) {
        // Polymorphic this type is Foo here, so it's OK to return a Foo.
        // Compiler is not aware of the class context, so we cast. 
        return new Foo(value) as this
    }
}

class Bar extends AbstractParent {
    // same as Foo
    create(value: number) {
        return new Bar(value) as this
    }
}

const foo = new Foo(42).times(2) // Foo
const bar = new Bar(42).times(3) // Bar
0 голосов
/ 05 ноября 2019

Вы можете передать дочерний тип в родительский конструктор и сохранить ссылку. Это препятствует тому, чтобы родитель знал обо всех возможных типах дочерних элементов (хотя я согласен с другим ответом, что может быть лучший способ достичь вашей общей цели). Примерно так ( детская площадка ):

type ChildConstructor<Child> = new (value: number, childConstructor: ChildConstructor<Child>) => Child;

abstract class AbstractParent<Child extends AbstractParent<Child>> {
    protected value: number;
    protected childConstructor: ChildConstructor<Child>;

    constructor(value: number, childConstructor: ChildConstructor<Child>) {
        this.value = value;
        this.childConstructor = childConstructor;
    }

    public times(multiplicator: number): Child {
        return new this.childConstructor(this.value * multiplicator, this.childConstructor);
    }
}

class Foo extends AbstractParent<Foo> {
    constructor(value: number) {
        super(value, Foo);
    }
}

class Bar extends AbstractParent<Bar> {
    constructor(value: number) {
        super(value, Bar);
    }
}

Обратите внимание, что для обеспечения 100% -ой безопасности типов у каждого дочернего класса должно быть хотя бы одно дополнительное частное свойство или метод для хранения TypeScript. «типизирование утки» из-за того, что классы считаются взаимозаменяемыми (поскольку они имеют одинаковые свойства и методы).

...