Use an Abstract Class as DI Token for your Services in Angular

Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 2 months ago

As your application grows, you may have different kind of implementations of the same service structure. Take for instance a logging service with different concrete implementation flavors. Something that might immediately jump into your mind is to use an interface to abstract the concrete implementation of these specific service instances and use the interface for fetching the instance in your components. Unfortunately, TypeScript interfaces won’t work with Angular’s dependency injector.

In this lesson we will learn why and how we can circumvent TypeScript interfaces.

Instructor: [00:00] Here, we have a simple people service which exposes a list of people, which we then inject here via the constructor in our people-is component, and we iterate over that list of people to visualize them here on our output.

[00:14] Obviously, that service has to be registered, in this case on the Angie module and the provider section. Note also that this is the short form for this one. The meaning here is that we have a token which, in this case is that type information.

[00:34] We tell the dependency injector, whenever you see that token, such as in this case in our people-is component, give us this instance of a class. Assume that that people service is used throughout your application, and potentially with different kind of implementations. For instance, a specific people service exposing just male people, and just female people, and so on.

[00:56] In that case, you probably want to generalize this kind of idea here of the people service a bit more, to have some kind of base class from which then the specific implementations inherit. The first idea that might come to your mind is to define an interface.

[01:12] We could define something like people service, getPeople, and below here, we call this the awesome-people service, which implements our interface. As a next step, we can then jump to our app module, and we could import here that awesome-people service as well, and do something like that.

[01:32] Whenever that people service is being found, inject the awesome-people service. In that case in some other module, we could say whenever that interface is found, inject our male-people service, for instance.

[01:45] If you look at the console here, you already note that we get a strange error message telling us that the people service is not defined, which might seem a bit strange initially, because we actually defined it here and we exported it. Everything seems to be fine.

[02:00] However, interfaces in TypeScript are actually only available at compile time. At runtime, they will be dropped. These serve just for a purpose of giving you type checking control and autocompletion. As a result, the dependency injector won't find it at runtime, and gives us this error.

[02:16] What we can do, however, is to define a class, in this case an abstract class. Obviously, we have to define also our method here as abstract, and then we can extend here from that abstract class. As a result, you can see that our application again works just as we expect.

[02:32] Interestingly, while it's absolutely recommended to extend from the base const here to get necessary compile-time checking, the Angular dependency injector doesn't require you to do so. We could simply drop this, and it would still work.

Enoh Barbu
Enoh Barbu
~ 6 years ago

I did not get your point with this. You should post concrete examples

Juri Strumpflohner
Juri Strumpflohnerinstructor
~ 6 years ago

The point here is about using a generic base type and specify concrete instances for it via the DI mechanism. This is a typical scenario when you deal with dependency injection. What I'm showing in this lesson is how you can achieve that, and especially that it's not possible to use interfaces, as many people wrongly assume. The reason is that TypeScript interfaces - as you might know - only exist at compile time and thus Angular cannot use them at runtime to determine the concrete instance of a class to inject. Thus, in this example I'm using an abstract class for this :)

Enoh Barbu
Enoh Barbu
~ 6 years ago

So it seems that this is a problem related to the ecosystem TS/ES compiling. It doesn't have anything to do with Angular...

Juri Strumpflohner
Juri Strumpflohnerinstructor
~ 6 years ago

Exactly 🙂

Markdown supported.
Become a member to join the discussionEnroll Today