A Successful IOC Pattern with Functions in TypeScript
Tags: javascript, typescript, ioc, functional
Published: 2021-06-20 19:12:41

---
title: A Successful IOC Pattern with Functions in TypeScript
published: true
description: 
tags: javascript, typescript, ioc, functional
//cover_image: https://direct_url_to_image.jpg
---

Over the past few months, I've been working on a TypeScript project, where I decided to challenge myself to use Functions only. This week, I refactored the codebase to use [IOC](https://en.wikipedia.org/wiki/Inversion_of_control) everywhere, and it feels like I *leveled up*. 😄

There's been a lot of articles these past couple of years about "functional programming" in JavaScript, and for some reason these are mostly concerned with immutability, sets, map/reduce, and so on. I come from a background of mostly OOP, where the answer to IOC is largely just "use constructors and interfaces", so this hasn't been really helpful.

What was missing for me, was a functional perspective on IOC and dependency injection.

In this article, I will try to illustrate the problems and solutions with a *silly* example for illustration purposes: for some reason, your boss wants the browser to display a personalized welcome message using an old-fashioned alert. Yikes. Well, whatever you say, boss, but I *expect* this requirement will change in the future.

> *To make the most of this article, you should know some basic TypeScript, and you should be familiar with the terms "inversion of control" or "dependency injection" - at least in the sense of using constructors and interfaces.*
> 
> *While* `DEV` *thinks this is "an 8 minute read", I recommend you open the [Playground](https://www.typescriptlang.org/play) and spend 20-30 minutes getting a feel for this.*

Okay, let's say you come up with function like this:

```ts
function showMessage(window: Window, message: string) {
  window.alert(message);
}
```

As you can see, I am already doing dependency injection. Rather than reaching out for the `window` global, this function asks for an instance of `Window`, which makes it easy to unit-test this function on a mock `Window` instance. So far so good.

💭 *So we're done, right?* 😁

Not quite.

Pretty soon, you will introduce functions that depend on `showMessage` - and, in order for another function to call `showMessage`, the other function needs to supply the `window` parameter - which means the dependency on `Windows` *spreads* to other functions:

```ts
function showWelcomeMessage(window: Window, user: string) {
  showMessage(window, `Welcome, ${user}`);
}
```

But wait, now `showWelcomeMessage` internally depends on `showMessage` - we really should use dependency injection for that too, right?

```ts
type showMessage = typeof showMessage;

function showWelcomeMessage(showMessage: showMessage, window: Window, user: string) {
  showMessage(window, `Welcome, ${user}`);
}
```

💭 *This looks wrong.* 🤨

`showWelcomeMessage` had to depend on `Window`, only so it could pass it along to `showMessage` - but it doesn't actually *do* anything with the `Window` object itself.

And while `showMessage` happens to use `Window` today, we might change that in the future, when someone realizes what a sad idea it was to use that alert. Maybe we decide to have it display a toast message on the page instead, and so the dependency changes from `Window` to `Document`. That's a breaking change. Now we have to run around and refactor everything that calls `showMessage`. 

Calling any function gets increasingly cumbersome - anytime any of the dependencies of any function changes, we have to manually correct the calls and introduce more dependencies everywhere. We're in *dependency hell*, and by now we're wasting most of our time refactoring.

💭 *There has to be a better way.* 🤔

My first realization was, why should someone who wants to call `showMessage` need to know anything about it's internal dependencies? What I really want, is a function that is *internally bound* to an instance of `Window`, so that the caller doesn't need to know or care.

That means we need a *factory*-function for the *actual* function:

```ts
function createShowMessage(window: Window) {
  return function showMessage(message: string) {
    window.alert(message);
  }
}
```

We'll need to extract the inner function-type - the one that has the `message` argument only, so that other units can depend on that:

```ts
type showMessage: ReturnType<typeof createShowMessage>;
```

(Note the user of [`ReturnType`](https://www.typescriptlang.org/docs/handbook/utility-types.html#returntypetype) here - you could have manually typed out the function signature of the inner function, but this helps avoid the duplication and the extra refactoring chore going forward.)

With that in place, our `showWelcomeMessage` no longer needs to care that `showMessage` internally uses `window`:

```ts
function showWelcomeMessage(showMessage: showMessage, user: string) {
  showMessage(`Welcome, ${user}`);
}
```

This also makes `showWelcomeMessage` easier to test, since now we don't need to mock `window` anymore - we can mock `showMessage` instead and test that it's being called. The code and the tests will now refactor much better, as they have fewer reasons to change.

💭 *So we're done, right?* 😅

Yeah, but No.

Consider now what happens to the next function up the call hierarchy. Let's say we have a `login` function, and showing the welcome message happens to be part of what it does - and we apply dependency injection here, too:

```ts
type showWelcomeMessage = typeof showWelcomeMessage;

function login(showWelcomeMessage: showWelcomeMessage, user: string) {
  showWelcomeMessage(user)
}
```

This problem doesn't go away by just fixing it at one level - we need to apply the same pattern we applied to `showMessage`, wrapping it in a `createShowMessage` factory-function. And what happens when something else needs to call `login`? Same thing again.

In fact, as you may have realized by now, we might as well apply this pattern consistently, as a convention, to every function we write.

💭 *Really? To every function?*

Yes, really - and bear with me, because it doesn't *look* pretty:

```ts
function createShowMessage(window: Window) {
  return function showMessage(message: string) {
    window.alert(message);
  }
}

type showMessage = ReturnType<typeof createShowMessage>;

function createShowWelcomeMessage(showMessage: showMessage) {
  return function showWelcomeMessage(user: string) {
    showMessage(`Welcome, ${user}`);
  }
}

type showWelcomeMessage = ReturnType<typeof createShowWelcomeMessage>;

function createLogin(showWelcomeMessage: showWelcomeMessage) {
  return function login(user: string) {
    showWelcomeMessage(user);
  }
}

type createLogin = ReturnType<typeof createLogin>;
```

It does what we wanted though. We can do all of our dependency injection from the top down now - we can now bootstrap everything from a single function in our entry-point script:

```ts
function bootstrap(window: Window) {
  const showMessage = createShowMessage(window);

  const showWelcomeMessage = createShowWelcomeMessage(showMessage);

  const login = createLogin(showWelcomeMessage);

  return {
    login
  }
}

// usage:

const { login } = bootstrap(window);

login("Rasmus");
```

Note that, in this example, `bootstrap` returns only `login` - if you have multiple entry-points, you can return more functions.

Now, as helpful as this pattern was, this approach to bootstrapping does not really scale well. There are two problems:

1. We're creating everything up front. In this simple example, we do need every component - but applications with multiple entry-points might only need some of the components, some of the time.

2. The code is very sensitive to reordering: you have to carefully arrange your factory-function calls, so that the previous function can be passed to the next. It requires a lot of thinking about dependencies.

We can solve both of these problems by *deferring* the creation of dependencies until they're required - that is, by making the calls to the factory-functions from within *another* function. Let's call this a *getter*-function.

Now, since these getter-functions could potentially be called more than once (although, in this simple example, they're not) we want them to return the *same* dependency every time - rather than generating new ones.

We can solve this by adding a tiny helper-function `once` to construct these wrapper-functions and [memoize](https://en.wikipedia.org/wiki/Memoization) the result:

```ts
function once<T>(f: () => T): () => T {
  let instance: T;

  return () => {
    if (instance === undefined) {
      instance = f(); // first call
    } 

    return instance;
  }
}
```

Let's refactor again: we'll wrap all of our initializations in closures and apply `once` to them - and our `bootstrap` function will now return the `getLogin` function.

(Note that the `once` function would generate [singletons](https://en.wikipedia.org/wiki/Singleton_pattern), if you were to call it from the global scope - but since we're calling it from the `bootstrap` function scope, new instances of all dependencies will be generated for every call to `bootstrap`.)

The new bootstrap-function looks like this:

```ts
function bootstrap(window: Window) {
  const getLogin = once(() => createLogin(getShowWelcomeMessage()));

  const getShowWelcomeMessage = once(() => createShowWelcomeMessage(getShowMessage()));

  const getShowMessage = once(() => createShowMessage(window));

  return {
    getLogin
  }
}

// usage:

const app = bootstrap(window);

const login = app.getLogin();

login("Rasmus");
```

I've purposely mixed-up the order of these getter-functions, to illustrate the fact that the order no longer matters: we're now free to arrange and group these lines in any order that makes sense - and we're also no longer creating anything before one of the getter-functions is actually called, which removes any concerns about potential future performance problems.

💭 *So we're...?*

Yes, done! 🏆✨

#### Footnote: When *not* to apply this pattern

You don't need to apply this pattern to every function. Some functions don't have dependencies, or maybe they depend only on standard JavaScript environment functions.

For example, there's no benefit to injecting the `Math.max` function, since that's a [pure function](https://en.wikipedia.org/wiki/Pure_function) with no side-effects. Whereas, on the other hand, there's a clear benefit to injecting `Math.random`, since a mock can return values that aren't actually random - making it possible to write predictable tests for your function.

### Bonus: Mutable State

I made one more little discovery this week that I'd like to share.

I think we've all been *here* one time or another?

```ts
let loggedInUser: string | undefined;

function setLoggedInUser(user: string) {
  loggedInUser = user;
}

function getLoggedInUser(): string {
  return loggedInUser;
}
```

It's dangerously easy and natural to do this in JavaScript. 💣

But even if you put this inside a module, this is *global state* - and it makes things difficult to test, since `setLoggedInUser` leaves behind in-memory state that persists between tests. (And you could write more code to clear out this state between tests, but, ugh.)

If you *must* have mutable state, we need to model that mutable `loggedInUser` state as a dependency, and then apply the create-function pattern described above.

```ts
interface LoginState {
  loggedInUser: string | undefined;
}

function createSetLoggedInUser(state: LoginState) {
  return function setLoggedInUser(user: string) {
    state.loggedInUser = user;
  }
}

function createGetLoggedInUser(state: LoginState) {
  return function getLoggedInUser(user: string) {
    return state.loggedInUser;
  }
}
```

I could have abbreviated this more, but I actually like seeing the word `state` here, clarifying the fact that a shared state is being either read or written.

It might be tempting to just take the previous version of this code, wrap it all in a single create-function, and return both of the functions, bound to the same `state` - but I wouldn't recommend that, because you could end up with *many* functions that depend on this state, and you don't want to be forced to declare them *all* in the same create-function. (Also, if you have to write a function that depends on *several* different state objects, that approach does not work.)

One more piece of advice: don't just create one big state object for all of your mutable state - this will muddy your dependencies, as functions will appear to depend on "the entire application state", even when those functions only actually depend on one property. (If you have multiple properties in the same state object, the [cohesion](https://en.wikipedia.org/wiki/Cohesion_(computer_science)) should be high - ideally 100%, meaning every function depends on *all* of the properties of that object.)

The `setLoggedInUser` function *does* have a [side-effect](https://en.wikipedia.org/wiki/Side_effect_(computer_science)), but now the effect is on state that you instantiate and control - making it easy to inject a new state for every test.

I'm not a functional programming Guru *yet*, and maybe there is more to learn here, but it's definitely a step up from global state. 🙂

### Conclusion

I feel like I've finally found a JS/TS code-style that really scales - both in terms of complexity and performance.

Applying this to my codebase has been an absolute breeze. I'm spending considerably less time juggling dependencies or refactoring things. Unit-testing is never a problem anymore.

For years, I've heard proponents of functional programming talk about the benefits - but the articles are mostly about arrays and immutability, which is great, and I've heard all the other great arguments. But it didn't really help me *write software*, and the outcome of prior attempts too often was either unmanageable or untestable. (But usually both.)

Unlocking this feels like the "next level" for me, and I really hope this puts somebody else on the path to more productive and scalable codebases with TypeScript or JavaScript.

Thanks for reading. Have fun! 😀✌