2

I get the current error:

Uncaught ReferenceError: can't access lexical declaration 'AbstractClass' before initialization

when doing this in a new Angular project:

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
  title = '';
  
  ngOnInit() {
    console.log(JSON.stringify(new (AbstractClass.get("MyId1"))()))
  }
}

And I can not see why this would happen since I first reference a static method, then instantiates the class reference that function returnes and then call a function on the newly initilized class...

AbstractClass.ts

export abstract class AbstractClass {
    abstract getModelName(): string;

    public static get(id: string): Type<AbstractClass> {
        switch (id) {
            case "MyId1":
                return MyId1ImplementationClass;
            case "MyId2":
                return MyId2ImplementationClass;
            default:
                return MyId1ImplementationClass;
        }
    }
}

MyId1ImplementationClass.ts

export class MyId1ImplementationClass extends AbstractClass {
    getModelName(): string {
        return "Id1!";
    }
}

MyId2ImplementationClass.ts

export class MyId2ImplementationClass extends AbstractClass {
    getModelName(): string {
        return "Id2!";
    }
}
4
  • 2
    Even if it might work, this is smelly by design: abstract class should not know its subclasses. Commented Sep 16, 2021 at 8:28
  • Agreed but since angular does not seem to support the type of DI I ideally would want then I need a "hacky" solution... :/ Commented Sep 16, 2021 at 8:49
  • You should use a regular factory pattern instead Commented Sep 16, 2021 at 9:08
  • @Bertramp I would really appreciate an example! :) Commented Sep 16, 2021 at 9:55

1 Answer 1

2

You have a circular dependency here that is almost certainly the cause of the error. Your implementation classes depend on the abstract class but the abstract class also depends directly on the implementations.

The fix the problem you have to break the circular dependency. One common way is to use a registry. This is a pattern in which the implementations add themselves to a map or list, which the static function can then access.

Something like:

export abstract class AbstractClass {
    abstract getModelName(): string;

    protected static registry = new Map<string, Type<AbstractClass>>()

    public static register () {
        this.registry.set(this.prototype.getModelName.call(null), this as any)
    }

    public static get(id: string): Type<AbstractClass> {
        return this.registry.get(id)
    }
}
export class MyId1ImplementationClass extends AbstractClass {
    getModelName(): string {
        return "Id1!";
    }
}

MyId1ImplementationClass.register()
Sign up to request clarification or add additional context in comments.

4 Comments

But then I need to have a different class that lists all the subclasses and tells them to register to the abstract class, right? Because they wont get created before accessed/initialized right?..
@Payerl You don't need another class. You just need to import the implementations somewhere. A good place is in the index.ts file for the module that exports AbstractClass. You just can't have AbstractClass.ts import them itself directly because that re-introduces the circular dependency.
@Payerl If AbstractClass isn't currently in a module with an index.ts it should be easy to make it so.
For completion I would suggest adding that you need to add the classes to be loaded in the app.module.ts files providers section: providers: [ MyId1ImplementationClass, MyId2ImplementationClass ], Otherwise it worked great! Thank you so much!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.