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.