I wrote a bit about constructor overloading in TypeScript a week ago.

My use case, is that I had a bunch of libraries for different UI Frameworks that shared a lot of code. I was creating a generic TypeScript library that all the other frameworks could use. In the process, I created some classes to be shared across frameworks. In Vue, these classes are explicitly instantiated and we control how constructors are passed in. In Angular, these classes would be services, and I wasn't sure if I would have control over how one service is injected into the shared class which would be a service.

My work around for this was to add multiple constructors into the class, using constructor overloading. The rest of this post will share some samples, and talk about how the overloading work within the context of an Angular service.

Create The Shared Class

First, I created the shared class, which has multiple constructor signatures:

view plain print about
1export class ClassWithMultipleConstructors {
2
3 classConstructorArg: ClassConstructorArgService | undefined;
4
5 constructor();
6 constructor(classConstructorArg: ClassConstructorArgService);
7 constructor(classConstructorArg?: ClassConstructorArgService) {
8 debugger;
9 if (classConstructorArg) {
10 this.classConstructorArg = classConstructorArg;
11 }
12 }
13}

This class has one property, a serviceToInject. It represents an object that is passed into the class upon creation.

This is the ServiceToInjectService:

view plain print about
1export class ClassConstructorArgService {
2
3 value = 'Default Value';
4 constructor() { }
5}

The constructor of our ClassWithMultipleConstructors has two method signatures, one with this class, and one without.

In TypeScript, It is super easy to create an instance of the class, and pass in the constructor arg:

view plain print about
1const instance = new ClassWithMultipleConstructors (new ClassConstructorArgService );

But, for services, Angular handles all this behind the scenes.

Create our Shared Classes as Angular Services

First, create the `ClassConstructorArgService` as an Angular Service. We can do this by extending the class without any metadata into a new class:

view plain print about
1@Injectable({
2 providedIn: 'root'
3})
4export class ServiceToBeInjectedService extends ClassConstructorArgService {
5}

Then create a new class that extends the ClassWithMultipleConstructors, and define it as a service using Angular's Injectable metadata, allowing it to be injected into the class automatically by Angular:

view plain print about
1@Injectable({
2 providedIn: 'root'
3})
4export class ServiceWithInjectInjectionService extends ClassWithMultipleConstructors{
5
6 override classConstructorArg = inject(ServiceToBeInjectedService);
7
8 constructor() {
9 super();
10 }
11}

This class overrrides the classConstructorArg property, and using the inject() keyword to set it's value to an existing Angular Service. This new Angular class extends the generic ClassWithMultipleConstructors, and the constructor accepts no arguments.

Run this in an Angular application, and you'll see the classes are created with the proper services injected.

Using Angular Constructor Injection

This is considered a bit old school, however you can also use Angular's Constructor Injection approach.

view plain print about
view plain print about
1@1@InjeInjcecttable(able({
{

2 providedIn: 'root'
3})
4export class ServiceWithConstructorInjectionService extends ClassWithMultipleConstr2uctors{
5
providedIn: 'root'
3})
4export class ServiceWithConstructorInjectionService e6 constructor(xtends ClserviceToInjectArg: ServassWithMultipleConiceToBeInjectedService) {
7 super(serviceToInjectArg);
8 }
9}

This class extends the ClassWithMultipleConstructors which has no Angular metadata. The method is injected by class name into the constructor and then passed into the super() constructor method to be set as a property variable inside the component.

Final Thoughts

When I started these experiments, I wasn't sure if the constructor overloading would work Angular's dependency injection framework. But, it works great; and helps me on this one project where we are sharing generic type script code across multiple frameworks.