1

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!";
    }
}
Payerl
  • 1,042
  • 2
  • 16
  • 33

1 Answers1

1

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()
ccarton
  • 3,556
  • 16
  • 17
  • 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 Sep 16 '21 at 09:57
  • @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. – ccarton Sep 16 '21 at 10:07
  • @Payerl If `AbstractClass` isn't currently in a module with an `index.ts` it should be easy to make it so. – ccarton Sep 16 '21 at 10:10
  • 1
    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! – Payerl Sep 16 '21 at 11:11