частичный инициализатор универсального класса

У меня есть много подобных классов, которые я хотел бы инициализировать со следующим синтаксисом:

class A {
    b: number = 1
    constructor(initializer?: Partial<A>) {
        Object.assign(this, initializer)
    }
}

new A({b: 2})

Я думаю, что возможность инициализации такими средствами является общим поведением, и поэтому я хотел бы изолировать эту логику, чтобы не повторяться в десятках файлов. Я пробовал это:

class Initializable<T> {
    constructor(initializer?: Partial<T>) {
        Object.assign(this, initializer)
    }
}

class A extends Initializable<A> {
    b: number = 1
}

new A({b: 2})

Это компилируется, но не работает, потому что неявное super() идет первым, поэтому b получает 2, как и требовалось, но затем получает 1.

Предлагает ли TypeScript типобезопасное решение для реализации такого поведения во всех моих классах?


person Yovar    schedule 21.08.2018    source источник
comment
Не решение, но очень важный ответ здесь.   -  person Robby Cornelissen    schedule 21.08.2018


Ответы (1)


Не существует простого способа запустить что-либо из базового класса после завершения работы конструктора производного класса. Единственное решение, которое я вижу (и я предлагаю другим придумать лучшее :)), — это использовать функцию, которая дополняет то, что станет классом A, вместо использования базового класса. В основном смешанный подход к добавлению функциональности.

function initializable<T extends new() => any>(cls: T) : {
    new (data?: Partial<InstanceType<T>>) : InstanceType<T> // add the constructor
} & Pick<T, keyof T> /* add statics back */ {
    return class extends (cls as any) {
        constructor(data?: Partial<InstanceType<T>>){
            super();
            if(data) { 
                Object.assign(this, data);
            }
        }
    } as any

}

const A = initializable(class {
    b: number = 2;
    static staticMethod() { }
    method() {}
});
type A = InstanceType<typeof A> // Optionally define the type for A to have the same effect as the class

var a = new A({b:1});
console.log(a.b) // output 1
a.method();
A.staticMethod();
a = new A();
console.log(a.b) // output 2
var aErr = new A({b:"1"}); //error

Примечание. Вмешательствам обычно не разрешается не изменять аргументы конструктора, и поэтому нам нужно немного помассировать типы, но это работает.

person Titian Cernicova-Dragomir    schedule 21.08.2018
comment
На данный момент я не понимаю весь этот код, поэтому я буду искать, но он очень прост в использовании и, похоже, работает так, как я хотел. Я думал об изменении класса, но я полагал, что это невозможно без потери сигнатур типов. Большое спасибо! - person Yovar; 21.08.2018
comment
@Yovar Добро пожаловать, если у вас есть какие-либо вопросы, не стесняйтесь спрашивать :) - person Titian Cernicova-Dragomir; 21.08.2018