Roll Your Own Dependency Injection

Roll Your Own Dependency Injection

What is Dependency Injection (DI)? Why use Dependency Injection? With TypeScript decorators and the JavaScript Proxy class — Dependency Injection is easier than you think. I’d like to show you what’s possible to achieve in a DI library in TypeScript in fewer than 300 lines of code (many of which are logging and comments).

With TypeScript decorators and the JavaScript Proxy class — it’s easier than you think

Have you tried using dependency injection (DI) frameworks only to be bowled over by their complexity and the difficulty of trying to bend them to your will? Use some of the popular DI libraries and you’d be forgiven for thinking it’s some branch of rocket science.

Well, I’d like to show that creating or using a DI library doesn’t have to be as complicated or difficult as you might think. After struggling through a couple of popular DI frameworks and not getting what I wanted, I decided to write my own.

In this blog post, I’d like to show you what’s possible to achieve in a DI library in TypeScript in fewer than 300 lines of code (many of which are logging and comments).

I created my own DI library for TypeScript that I use in Data-Forge Notebook, which runs on Electron with a user interface built using React. I wanted a DI library that worked well for both React and for generic TypeScript code, and I wanted something that was simple to understand and simple to use, but still very flexible.

I’ve now open-sourced my DI library, and it’s called Fusion. You can find it on npm and GitHub. Please download a copy of the code so you can follow along with this blog post.

What is dependency injection?

Have you ever struggled to keep your application working as it grows and its complexity ramps up?

All software is composed of interacting components that are wired together in particular ways. As the number of interacting components grows, the wiring and the number of connections between components grows exponentially (a phenomenon known as Metcalfe’s law).

Do you really want to look after all that complicated wiring yourself? This is what automated dependency injection will do for you.

Dependency injection is a design pattern for automatically wiring together the components of your complex application. This is especially useful when writing dependencies through to the leaves of a deep and complex user interface hierarchy, which you can imagine after looking at figure 1.

Using DI To Directly Wire Dependencies To The UI

Figure 1: Using dependency injection to directly wire dependencies through to a deep and complex UI component hierarchy.

Why use dependency injection?

Dependency injection is useful for a variety of reasons:

  • The concerns of dependency use and dependency resolution are separated
  • It encourages separation of concerns and loose coupling between components
  • It automates the wiring of components in your application, a task that can otherwise be very complicated
  • Dependencies are easier to mock for unit testing
  • It facilitates fast rewiring of components, which makes refactoring quicker and safer
  • It allows for abstractions that help reuse the same codebase across multiple platforms, environments, or types of processes
  • It can be used to centralize or externalize the configuration of your application, although that’s not something that Fusion supports at this stage

Dependency injection is also simply a clean and elegant way to structure your application. Using DI, you’ll spend less time concerned with system wiring and more time developing features.

What are the alternatives?

Before striking out and writing your own code library, you should always consider the alternatives first. How else can we wire up dependencies?

Instead of injecting our dependencies, we can create them directly in the code where they are needed. We don’t want to do that, though, because that means we can’t share dependencies, and it’s also impossible to replace the dependencies with mock versions for unit testing.

Another option is to directly access singletons, but that has the exact same consequence with the addition of hard-linked global variables.

A simpler alternative to DI that you might like is called the service locator pattern. It’s similar to DI but much less automatic. It’s still a great pattern to start with if you think DI is too complicated.

Of course, there are plenty of existing DI libraries you could use. I just searched npm and found 1,778 packages! Surely there is something here that I can use?

Well, like I said, I’ve tried some of the popular libraries and found them wanting. They are often very convoluted and can be difficult to bend to your needs.

I wanted something much simpler. I wanted a DI library that I could make work just the way I wanted. That’s why I created Fusion. Now I’d like to show that rolling your own simple DI library is not that difficult.

Dependency injection with TypeScript decorators

Fusion is built on TypeScript decorators and the JavaScript [Proxy]( class. It creates a proxy for each class to intercept calls to the constructor. The proxy automatically injects dependencies just after an object is constructed, but just before the object is returned to be used by the caller.

As an example, we’ll use a React component called SetupWindow. Figure 2 indicates how the dependency injection system provides values for the properties of our class. This is a simplified but real example taken from Data-Forge Notebook. Please note that Fusion works with any TypeScript class — I’m demonstrating with React to show that it can be done.

DI Automatically Provides Values For Certain Properties

Figure 2: Dependency injection automatically provides values for certain properties in our classes.

typescript javascript developer

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

How long does it take to develop/build an app?

This article covers A-Z about the mobile and web app development process and answers your question on how long does it take to develop/build an app.

Builder Pattern in JavaScript/TypeScript

This article is not just about Builder Pattern in JS/TS, I will be explaining my thought process behind it and see if you can relate yourself to this. I believe everybody has a unique way of solving problems, hope you get something to learn from it.

TypeScript will Make You a Better JavaScript Developer

TypeScript will make you a better JavaScript developer. You'll feel confident writing code, have fewer errors, and write fewer tests (yay!). Find out why.