Unknown number of arguments

So far we can type curried functions of n-th arguments (we only defined 4-th, but you get the idea), but the curry function must work with functions of any lenght, so we must have a way to indicate TypeScript which CurryN type to use, we need conditional typings.

Conditional typings is a feature added in version 2.8 of TypeScript, and they allow us to set a type depending on a condition. It works like the ternary operator but with types. In the following example we can notice that the type of x will depend on whether TypeX matchs or not with SomeShape.

So in our case we’ll define a conditional type called VariadicCurry which will help us determine which CurryN type to use. The name comes from Variadic Functions which are functions that can receive a variable number of arguments. It works by nesting the type condition, so if T is a tuple of four numbers it will return a Curry4 type, if its a tuple of three, Curry3, and so on.

If the type T doesn’t match with any of the tuples, we return the new unknown type introduced in TypeScript 3, wich is similar to any, but safer in the way that you can’t assign it to every variable, you need to cast it, and ideally after checking it’s type dynamically.

Now we have all the tools to define our curry function 🎉.

There is a lot going so lets break it in pieces. The function curry accepts only one argument named fn, which is the function to be curried. The return of curry will be of type CurryN depending on wheter T is a 1-tuple, 2-tuple, etc. We know T will be an n-tuple because we defined it inside the angle brackets <T extends number[]>. And we define that fn will be a function of n-th arguments (defined by …args: T) that returns a number.

The last part is possible thanks to another set of features added in version 3, which allows you to interact with function parameter lists as tuple types.

With all this in place we have defined the types for our curry function… that only works with numbers. ¯\_(ツ)_/¯️

Generic parameters

Numbers are great, but we made it this far, lets try to make the function work with all types. We’ll start by making the the return type generic on R. We’ll refactor our code making fn return a type we know nothing about called R. This type propagates all the way trough our other definitions, making each CurryN generic on the return type.

Making all CurryN generic in their arguments its fairly easy as well, we just have to make our types to accept extra parameters.

And to make VariadicCurry and curry to use the new parameters we need to do one more “trick”.

The VariadicCurry type will now match T to a n-tuple ignoring the argument type (because we use any instead of number) and only caring about the arity of the tuple. When we return a specific CurryN we use the indexed types T[0], T[1], T[2] and T[3] to specify the correct arguments types.

And that’s it! We now have the typings for a curry function that works with an unknown number of arguments and any type you may think. If you want to try the full code you can browse this repo or use this playground.