TypeScript Pain Points: Compose Function

Kirill Novik
4 min readDec 18, 2022

--

Composing functions

I’ve been working with TypeScript for a while now and have to admit that it is one of my favorite tools as it makes many day to day dev tasks much easier.

However, when TypeScript meets React or Redux things get a bit ugly. Types associated with React components and logic are notoriously messy.

Typing higher order components (HOCs) and composing them is yet another level of messy.

In this article, I will attempt to examine one specific use case related to TypeScript and Redux that I believe is very important because once this use case gets resolved, then TypeScript would get to a whole new level both in terms of functionality and adoption.

Problem Statement

The problem is that currently there is no good way of typing a compose function in TypeScript.

Let’s declare some decorator functions:

Compose function is a function that turns this:

Into this:

It’s a best practice and experience shows that it is indeed much easier to work with compose signature than with nested functions.

Unfortunately, in terms of TypeScript, currently, there is no good way to do typing for the compose function:

TS inference for nested functions works well
TS inference for Redux compose function doesn’t work…

Background

If you look what’s under the hood, you will see that compose is typed like so:

The problem with these definitions is that the implicit inference in TypeScript works from left to right (A => B, B => C, C => D ), but here it’s right to left (C => D, B => C, A => B). This means that right-to-left inference has to be explicitly typed by you.

This pretty much kills the beauty of typing as it now requires you to do this by hand which is quite painful and contributes to poor dev experience.

In Search of a Solution

I found a couple of resources about typing the compose function (here, here, here, here, and some resources about flowtype $Compose). But none of these solutions really work and at best provide the return type of the last function to be called.

I tried to hack something on my own and came up with a super hacky solution, which, surprisingly, does work.

The hack I use relies on:

  1. TypeScriptconst assertion
  2. left-to-right pipe function declarations
  3. Recursive type for reversing a readonly tuple.
  4. Auxiliary calculation declarations

So, to elaborate:

  1. If we type function array as a readonly tuple, we will be able to spoon-feed it to TypeScript type system to make it correctly calculate input-output types
Readonly tuple of functions for easy digestion

2. left-to-right pipe function declarations will allow us to correctly compute the return type

Pipe function declarations

3. Now we will need to reverse the readonly tuple type, because pipe takes arguments in reverse order compared to compose

Reverse readonly tuple type

4. Lastly, we need to compute the result of the compose function. Unfortunately, it has to be done explicitly.

In the end we can see that this setup correctly infers the type.

The type is correctly inferred

The code outlined above can be found here

Future Work

Even though it works, this approach is not acceptable because it is way too hacky.

This functionality needs to be available out of the box in TypeScript.

I think if TypeScript enhances recursive types with the ability to pass output type to input of another function, it would allow to correctly implement typing for composeand pipe.

As for the future work, I am planning to go under the hood into the inner workings of the the TypeScript typechecker API, and, if I’m able to figure it out, I might create a PR with a fix to this issue.

Thanks for reading, subscribe, leave a couple of claps and let’s hope this gets fixed soon.

--

--

Kirill Novik

Whether I shall turn out to be a hero of this book these pages must show