TypeScript: наследование рекурсия

#typescript #inheritance

#typescript #наследование

Вопрос:

Пожалуйста, посмотрите на пример ниже.

 class A {
    public nodes: A;
    constructor(data, instance: new (data) => any) {
        this.nodes = data.children.map(child => new instance(child));
    }
}

class B extends A {
    public nodes: B;
    constructor(data) {
        super(data,B);
    }
}

class C extends A {
    public nodes: C;
    constructor(data) {
        super(data,C);
    }
}
 

Я хочу создать узлы во всех классах, унаследованных от класса A, определив создание этих узлов в классе A. Теперь я сделал это, как написано выше. Я хочу знать, есть ли более приятный способ сделать это?

Комментарии:

1. Обеспечьте ввод для data instance переменных и, и это было бы хорошо.

2. @zerkms К сожалению, я не могу установить тип для instance , потому что это может быть любой унаследованный класс из класса A (то же data самое, потому что это зависит от класса)

3. Ваш код вообще работает? Потому что, просто прочитав его, я не уверен, что это так. Если вы вызываете new B(data) это, вызывается суперконструктор, и суперконструктор new B снова вызывается (как new instance ). Который должен логически вызывать суперконструктор и переходить в бесконечную рекурсию. Или, может быть, просто выдать ошибку. Я полагаю, это зависит от того, какую форму data имеет. И я все еще не уверен, почему вы здесь не используете дженерики. Это не похоже на связь между A , B , и C . Вам может понадобиться композиция для решения вашей проблемы проектирования в первую очередь.

4. @VLAZ относительно бесконечной рекурсии: new instance вызывается только для дочерних узлов (таким образом, он вызывается до тех пор, пока каждый последующий узел имеет дочерние узлы). о том, почему я не использую generic: запись мне нравится new B <B> (data) или new C<C>(data) выглядит странно. Но в то же время я все равно хотел бы знать, какое из решений является лучшим…

Ответ №1:

О том, почему я не использую generic: запись like new B<B>(data) или new C<C>(data) выглядит для меня странно.

Для этого вам нужно использовать дженерики, но B и C не обязательно должны быть универсальными. Они могут расширять определенные типы общей базы A . Так что вы просто позвоните new B(data) .

Мы можем получить некоторые из требуемых типов, просто посмотрев на использование.

 this.nodes = data.children.map(child => new instance(child));
 

Эта строка здесь сообщает мне, что data переменная, которая передается конструктору, имеет children свойство. children представляет собой массив , в котором каждый элемент может быть присвоен тому же типу , data что и .

children кажется, здесь требуется, и в этом случае терминальные узлы будут иметь {children: []} . Это нормально, если это то, что вы хотите, но обычно это необязательно, так что конечные узлы вообще не имеют children свойства.

 type Recursive<OwnData> = OwnData amp; {
    children?: Array<Recursive<OwnData>>;
}
 

Таким образом, мы получаем этот тип Recursive<OwnData> , который является общим, который зависит от переменной OwnData , где OwnData представлены все свойства, отличные от children .

Мы не знаем, что происходит внутри конструкторов отдельных потомков A , и мы не знаем, какие уникальные переменные экземпляра они могут объявлять. Таким образом, для максимальной гибкости мы должны предположить, что свойства data узла и свойства узла не всегда одинаковы. Это означает, что A требуется два обобщения.

 class A<DataProps, NodeType> {
    public nodes: Array<NodeType>;

    constructor(
        data: Recursive<DataProps>,
        instance: new (data: Recursive<DataProps>) => NodeType
    ) {
        this.nodes = data.children ? data.children.map(child => new instance(child)) : [];
    }
}
 

Теперь, когда мы создаем подкласс, мы расширяем определенную версию A<DataProps, NodeType> . Мы можем передать сам подкласс в качестве типа для NodeType .

 type BData = {
    b: number;
}

class B extends A<BData, B> {
    public b: number;

    constructor(data: Recursive<BData>) {
        super(data, B);
        this.b = data.b;
    }

    someFunc(): void {
        console.log(this.b);
    }
}

const bInst = new B({b: 1, children: [{b: 2}, {b: 3}]});
// each node of `bInst` is `B`
bInst.nodes.map(b => b.someFunc() );
 

Ссылка на игровую площадку Typescript