Как из конструктора абстрактного класса использовать переопределенное поле из дочернего класса (typescript)
Мне нужно создать иерархию из одного абстрактного класса и нескольких унаследованных от него классов. У каждого класса есть свое уникальное имя (поле NAME), которое используется для инициализации большого количества параметров при создании экземпляра (для простоты заменил просто на метод printName). Эта логика одинакова для всех классов, поэтому мне нужно вынести ее в конструктор абстрактного класса. А при создании нового конкретного класса должно быть достаточно реализовать одно простое поле или несложный метод (новые классы могут создаваться другими разработчиками, нужно минимизировать их настройку).
abstract class AClass
{
protected abstract NAME:string;
constructor() {
this.printName();
}
private printName = () => {
console.log(this.NAME);
}
}
class ImpClassOne extends AClass
{
protected NAME: string = 'name1';
}
class ImpClassTwo extends AClass
{
protected NAME: string = 'name2';
}
const one = new ImpClassOne();
// Ожидаю в консоли 'name1'
const two = new ImpClassTwo();
// Ожидаю в консоли 'name2'
// В обоих случаях получаю 'undefined'
Но новые имена не подтягиваются. Сначала отрабатывает конструктор абстрактного класса, который использует только свой экземпляр поля NAME (а оно undefined), а только потом конструктор нового класса (который уже видит правильное имя, но поздно, потому что настройка была проведена в суперклассе).
И вот вопрос, как в суперклассе брать переопределенное имя из дочернего класса? Или какую тогда структуру для вызовов выбрать, чтобы все заработало? Главное - при создании нового класса должно быть минимум настроек. В других языках я такой проблемы не помню, вроде суперкласс сразу всё подтягивал. Думал о паттерне "шаблонный метод", но из конструктора вызвать абстрактный метод тоже нельзя. Заранее спасибо.
Ответы (2 шт):
Я бы использовал статические поля (но в ES5 до них хуже добираться): playground
abstract class Base {
protected static declare NAME: string;
constructor() {
this.printName(new.target.NAME);
}
private printName(name: string) {
console.log(name);
}
}
class Child1 extends Base {
protected static NAME = 'name1';
}
class Child2 extends Base {
protected static NAME = 'name2';
}
const x = new Child1();
const y = new Child2();
А есть способ, при котором можно и потом обращаться к этому полю? То есть из конструктора через
new.target.NAME, а потом, не из конструктора, просто черезthis.NAME? Ведь оно может понадобиться и после отработки конструктора.
Ну сохранить в поле: playground
abstract class Base {
protected static declare NAME: string;
protected NAME: string;
constructor() {
this.NAME = new.target.NAME;
this.printName();
}
private printName() {
console.log(this.NAME);
}
}
class Child1 extends Base {
protected static NAME = 'name1';
}
class Child2 extends Base {
protected static NAME = 'name2';
}
const x = new Child1();
const y = new Child2();
Можно сделать через геттер или метод, но мне это не нравится: playground
abstract class Base {
protected abstract readonly NAME: string
constructor() {
this.printName()
}
private printName() {
console.log(this.NAME)
}
}
class Child1 extends Base {
protected get NAME() { return 'name1' }
}
class Child2 extends Base {
protected get NAME() { return 'name2' }
}
const x = new Child1();
const y = new Child2();