Dependency Injection in TypeScript

Dudu Popkhadze
Level Up Coding
Published in
3 min readNov 8, 2021

--

Motivation

For the past few years, Node became one of the most popular back-end solutions. It’s very easy to bootstrap an app on Node and start handling HTTP requests on the fly. But there is a problem, in most cases, Node apps get very complicated and coupled while they grow, so keeping domain and persistence layers separate becomes extremely hard.

To resolve this problem, we should dive into the world of SOLID architecture (one part of it for today). To write code according to SOLID guidelines, we need Dependency Injection. It allows us to abstract the creation of classes to be separate from their implementation, which separates domain/business logic from all other layers and makes code easier to maintain.

There is no out of box solution for dependency injection in Node but we can implement our own DI service with help of TypeScript decorators.

Let’s dive in.

Implementation

Since decorators are still in experimental mode, we will need to enable them in tsconfig.json

{  "compilerOptions": {    "target": "ES5",    "experimentalDecorators": true,    "emitDecoratorMetadata": true,    "outDir": "dist"  }}

Let’s suppose we have a back-end application that is in desperate need of Logger. Logger can have many implementations, some can log messages directly in the console, some can store them in a database and others can be doing both at the same time.

Let’s define generic Logger abstract class which will be implemented by actual Loggers

Nothing fancy here, just an abstract class with single log method. Now let’s take a look at possible Logger implementations.

For each dependency, we will need a unique string identifier that will be mapped to the actual class instance

Now we need some kind of manager class, which will store all initialised classes and inject them whenever dependant classes will require them.

We have Logger classes we want to inject and DependencyManager which stores all needed mappings. The only thing that remains is @Inject decorator (read more here about TypeScript decorators), which will take care of injecting actual instances of classes with help of our Dependency Manager

At this point we are ready to use our inject decorator and see our code in action

Output:
LogServiceA -> test
LogServiceB -> test

We can also define injectable classes with easily with @Injectable decorator, since we already implemented @Injectable we just need to add a call with a unique id on top of the class declaration

Output:
LogServiceC -> test

As you can see, Injectable also works as intedended.

Summary

This was a small intro to Dependency Injection in Node. Dependency injection does go much deeper than this, such as injecting directly into the constructor and using symbols instead of tokens (similar to how Angular does it). However, I hope this has given you a small insight into how Dependency Injection works behind the scenes. When I first started coding Dependency Injection was a black box that I knew was very helpful but had no idea how it worked, I hope after this article there is one more magical part about software engineering that you’re no longer in the dark about.

Code available here.

Happy Hacking!

--

--