Advanced Typescript: Typing Partially Applicable Functions
Tags: typescript, functional
Published: 2023-06-02 11:53:43

This post follows up on [How to make functions partially applicable in JavaScript](https://dev.to/ycmjason/how-to-make-functions-partially-applicable-in-javascript--416b).

Partial Function Application allows functions to collect its arguments in multiple function calls, returning the result once all arguments are collected. For example, `f(x, y, z)` can be called as `f(x)(y, z)` or `f(x, y)(z)` etc.

Some people might confuse this with currying. However, currying is a slightly different concept as it only refers to turning a function with N arguments into N functions that takes in 1 argument. E.g. `f(x, y, z)` into `f(x)(y)(z)`. 

In this article, we will explore how we can define the type `PartiallyApplicable<F>` that would allow `F` to be applied partially.

## TL; DR

```ts
// basically dropping the last element until we have exactly 1 element, this keeps the label of the array
type GetFirstParam<Params extends any[]> = Params extends [any]
  ? Params
  : Params extends [...infer X, any]
  ? GetFirstParam<X>
  : never;

type GetRestParam<Params extends any[]> = Params extends [any, ...infer Rest]
  ? Rest
  : [];

type PrependArg<
  F extends (...args: any[]) => any,
  X extends [any]
> = F extends (...args: infer Args) => infer ReturnType
  ? (...rest: [...X, ...Args]) => ReturnType
  : never;

type PartiallyApplicableUnion<F extends (...payload: any[]) => any> =
  F extends (...args: infer Args) => infer ReturnV
    ? Args extends []
      ? F
      : Args extends [any]
      ? F | (() => PartiallyApplicable<F>)
      :
          | (() => PartiallyApplicable<F>)
          | ((
              ...args: GetFirstParam<Parameters<F>>
            ) => PartiallyApplicable<
              (...argRest: GetRestParam<Parameters<F>>) => ReturnV
            >)
          | PrependArg<
              PartiallyApplicableUnion<
                (...argRest: GetRestParam<Parameters<F>>) => ReturnV
              >,
              GetFirstParam<Parameters<F>>
            >
    : never;

type UnionToIntersection<U> = (
  U extends any ? (arg: U) => any : never
) extends (arg: infer I) => void
  ? I
  : never;

type PartiallyApplicable<F extends (...payload: any[]) => any> =
  UnionToIntersection<PartiallyApplicableUnion<F>>;

// Example:

// notice how the argument names are now preserved
declare const f: PartiallyApplicable<
  (a: number, b: string, c: boolean, d: Date) => string
>;

// all evaluates to string
f(0, 'a', true, new Date());
f(0)('a', true, new Date());
f(0, 'a', true)(new Date());
f()()(3)()('a', true)()()(new Date());
```

[TS Playground](https://www.typescriptlang.org/play?noUncheckedIndexedAccess=true&pretty=true&noErrorTruncation=true#code/PTAECMEMGcEsGNIBskE9QBMBOB7ADnrAHYDmoALgBYCmoSM5o1S1AttUYwK6exKgB3WpUgA3WtQAekeOTSgAjExbtOAGgqVY0UAGtq1PDqq164ZqBwAzTbUhYskVAChyqPLQDi1cgDFYWNDkAAr2kKwAPKGOrDpS5BwYOpBEqADaALoAfKAAvKDR4XGSCURJoGkpqBnOoKAA-AVhsbWgAFxNMcWl5WkAdAPEVtRYoAAaGlU1dY3efgFBhZFjWa0dRNTiWADczq7uXj4AStSLzVHN3YnJqZk5+UtXZTqVqRoDfUMjoCdB0w0-U7kNYVDK7fYeApYQyJACCWBIEVaviYJWuoAAFB97CRoB0qpkAJR5HJVNStMaonovKbOe6gFHxdFYgY4vGgL6jeG44m5HKcwHkLhYIgAFQOrUaLL60KCHX6AwmoA+3OgGV5OROQpF4o8II2W3Bbkh0XIsGQaFhBCQCEg4BYAFUiLAcEQIoy0c9MR88E4kDhIBh8bd1STQFV7siqczsQj2QLVRqOURhqMtcKiAA1VozUCq6NezI5uoA3zFuodfNMwu0kslxoogA+mIxSdN5pQqCteBtiHt1HdWUJ5faI7rzYxrbD7YtXettv7g+HdZXoAnGLHq+VrLjHTm-kCIXOSx8I2gg9WW5XbfsZtn3d7dpYSKvq+lON+5D3xyBSwuMVPQILyTdMRWzV8SyHTc6mbYJoQ8MpuRfCCSxnTsHwXR1nVdZCUJLd8EU-b9yE-P8TwSIDfCyIcw1ArNoJXLJyTwup9wWI8Yn-cJAPPKjLxQ-iK1AA0RiNA5QCdF0xRwABJTgz2oWQpIiB16Q3OoHQLcoqgBDEcQ6B0kx09ZNhGZxiWrco9IRDoBRkpNRBwWAMElUAZP1Uydj2Y1aDQy15z7Z8PWpb0Bl9VB-UDYN0lDPlw1SSMNOw6S5Io6BFLNHC-LnHtMOoSScL48EQFAABRaRWB7ag2j2EqiBwM14GEHABFscMES4VRGCIbjkmhYSWtAPBZRGcQXIwRT6H6+BXSCUArA6bKMMCgdWj09ZOvMLANHADogiwYgSA0eAOnAHAcBYFINCDUAABFIASJN9sOulirAC0mFEZAuAe04KBwUBntIZwrAxAAGDQAHJIEhjRyCwLhqA0DZWvuhJW0JXZQbBwkMWh2GKARpHhOoVHfoxrHwahmG4aJ3GUbu8nCUxkHWzZgBmXHcfx2nEa5tmGbR6gKecIA)

## Recap: How to make functions partially applicable in JavaScript

In the [previous article](https://dev.to/ycmjason/how-to-make-functions-partially-applicable-in-javascript--416b), we've explored how we can make any functions partially applicable.

```js
const enablePartialApplication = (fn) => (...args) => {
    if (args.length >= fn.length) return fn(...args);
    return enablePartialApplication(fn.bind(null, ...args));
};

const f = enablePartialApplication((a, b, c) => a + b + c)
f(1)(2, 3) // 6
f(1, 2, 3) // 6
f(1, 2)(3) // 6
f(1)(2)(3) // 6
```

This function either return the result of `fn`, if the provided `args` matches the number of arguments `fn` accepts, or it returns a partially applicable function that takes in the rest of the arguments.

## Goal of this article

The goal of this article is to present a step-by-step approach to tackle type challenges like this. The following sections are progressively more complicated, but my objective is to ensure that anyone can take away something valuable from this article, even if you don't fully comprehend the final conclusion.

## Typing the `add` Function

[Function overload](https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads) is a way to define multiple call signature of a function. For example:

```ts
function add(x: number, y: number): number; // first overload
function add(x: number): (y: number) => number; // second overload
function add(x: number, y?: number): number | (y: number) => number {
  if (typeof y === "undefined") return (y: number) => x + y;
  return x + y;
}

add(1)(2); // using the first overload
add(1, 2); // using the second overload
```

In fact, function overload can be expressed with `&`. For example:

```ts
type Add = ((x: number, y: number) => number) &
  ((x: number) => (y: number) => number);

const add = ((x: number, y?: number): number | ((y: number) => number) => {
  if (typeof y === "undefined") return (y: number) => x + y;
  return x + y;
}) as Add;
```

Note that in here we need to use `as Add` since TypeScript wouldn't be able to figure out in which case the function returns a function or a number. In the following article, we will be using the latter syntax as it allows more flexible composition of function overloading.

## Typing 2-ary Functions

> A 2-ary function, also known as a binary function, is a function that takes exactly two inputs or arguments.

In this section, we'd define `PartiallyApplicable2<F>`:

```ts
type PartiallyApplicable2<F extends (x: any, y: any) => any> = ...
```
With this, we can simply define `Add` as `PartiallyApplicable2<(x: number, y: number) => number>`.

We can tackle this relatively easily. With the help of `infer`, we can define the type as

```ts
type PartiallyApplicable2<F extends (x: any, y: any) => any> = F extends (
  x: infer X,
  y: infer Y
) => infer R
  ? ((x: X, y: Y) => R) & ((x: X) => (y: Y) => R)
  : never;

type Add = PartiallyApplicable2<(x: number, y: number) => number>;
```

This is relatively simple. Here is a breakdown:

1. Inferring the argument and return types
     ```ts
     type PartiallyApplicable2<F extends (x: any, y: any) => any> = F extends (
       x: infer X,
       y: infer Y
     ) => infer R
       ? [if-case]
       : [else-case]
     ```
2. We know that `F` must be a function that takes in 2 argument and return something (based on the `F extends (x: any, y: any) => any` constraint), so we can put `never` in the `[else-case]`.
     ```ts
     type PartiallyApplicable2<F extends (x: any, y: any) => any> = F extends (
       x: infer X,
       y: infer Y
     ) => infer R 
       ? [if-case]
       : never
     ```
3. In the `[if-case]`, we know that there are 2 posible ways to call a 2-ary partially applicable function. You can either call the function with all 2 arguments or 1 after another. So we can simply put `((x: X, y: Y) => R) & ((x: X) => (y: Y) => R)`:
     ```ts
     type PartiallyApplicable2<F extends (x: any, y: any) => any> = F extends (
       x: infer X,
       y: infer Y
     ) => infer R
       ? ((x: X, y: Y) => R) & ((x: X) => (y: Y) => R)
       : never;
     ```

With `PartiallyApplicable2` we can type any arbitrary 2-ary function easily. 

## Typing N-ary Function

> In computer science, N-ary functions are commonly used to represent operations that take a variable number of arguments.

Typing 2-ary functions is easy. Let's take a step further and think about how we can create a general `PartiallyApplicable<F>` type that will facilitate any N-ary function.

Before we dive into any code, let's figure out what `PartiallyApplicable<F>` should do in from a 0-ary to 4-ary functions:

In the following, I would use some psudo-TypeScript to describe function signature. Specifically, `(A, B) => R` to represent `(a: A, b: B) => R`. This reduces noise and make things easier to understand.

For illustration purpose, I would ignore the case where you can pass no argument to the partially applicable function, except for the 0-ary function. We will discuss how to deal with this case in a later section.

- 0-ary function: `PartiallyApplicable<() => R>`
	- There is only 1 way to call this function:
		1. `() => R`
- 1-ary function: `PartiallyApplicable<(A) => R>`
	- There is only 1 way to call this function:
		1. `(A) => R`
- 2-ary function: `PartiallyApplicable<(A, B) => R>`
	- 2 overloads:
		1. `(A, B) => R`
		2. `(A) => (B) => R`
- 3-ary function: `PartiallyApplicable<(A, B, C) => R>`
	- 4 overloads:
		1. `(A, B, C) => R`
		2. `(A, B) => (C) => R`
		3. `(A) => (B, C) => R`
		4. `(A) => (B) => (C) => R`
- 4-ary function: `PartiallyApplicable<(A, B, C, D) => R>`
	- 8 overloads:
		1. `(A, B, C, D) => R`
		2. `(A, B, C) => (D) => R`
		3. `(A, B) => (C, D) => R`
		4. `(A, B) => (C) => (D) => R`
		5. `(A) => (B, C, D) => R`
		6. `(A) => (B, C) => (D) => R`
		7. `(A) => (B) => (C, D) => R`
		8. `(A) => (B) => (C) => (D) => R`

As you can see, the number of overloads grows exponentially as the number of arguments goes bigger.

Here is an illustration of a 4-ary function:
![](https://i.imgur.com/teUPpd0.png)

Consider only the node: `remaining: B, C, D` and the paths involved.

![](https://i.imgur.com/p62UM9Q.png)

In fact, this tree that's currently highlighted is exactly `PartiallyApplicable<(B, C, D) => R>`! It contains 4 "routes" to get to `R`, namely:

1. `(B) => (C) => (D) => R`
2. `(B) => (C, D) => R`
3. `(B, C) => (D) => R`
4. `(B, C, D) => R`

This gives us a clue that `(A) => PartiallyApplicable<(B, C, D) => R>`  would define half of `PartiallyApplicable<(A, B, C, D) => R>`!

Now let's take a closer look to the left-hand side of the tree:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x99yffdr3eq28rtyuxuk.png)

It actually shares most of the bits as the tree spanning from the node `remaining: B, C, D`. The only difference being the first call signature:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/newtoj2u777r47fivg41.png)

Notice the symmetry of the lines coming from node `remaining: A, B, C, D` and the node `remaining: B, C, D`. The minimum edit to get from the right 3 lines to the left 3 lines is to append `(A, ...)` to each line!

1. `(B)` becomes `(A, B)`
2. `(B, C)` becomes `(A, B, C)`
3. `(B, C, D)` becomes `(A, B, C, D)`

We will use an imaginary utility type (see latter section on how we define this) called `PrependArg<F extends (...args: any[]) => any, X>`, that will prepend `X` to the parameters of `F`. E.g. `PrependArg<(A, B) => R, X>` would equal to `(X, A, B) => R`.

Now, we can get the left-hand side of the tree by simply doing `PrependArg<PartiallyApplicable<(B, C, D) => R>, A>`. This should yield:

1. `PrependArg<(B) => ..., A>` will become `(A, B) => ...`
2. `PrependArg<(B, C) => ..., A>` will become `(A, B, C) => ...`
3. `PrependArg<(B, C, D) => ..., A>` will become `(A, B, C, D) => ...`

To conclude this section, we have discovered that `PartiallyApplicable<(A, B, C, D) => R>` is basically:

1.   `((A) => PartiallyApplicable<(B, C, D> => R>) &`
2.  `PrependArg<PartiallyApplicable<(B, C, D) => R>, A>` 

In fact, this pattern applies to any N-ary functions, for N > 1. So we can generalise this and say `PartiallyApplicable<(A, ...RestArgs) => R>` equals to:
```
((A) => PartiallyApplicable<(...RestArgs> => R>)
	& (PrependArg<PartiallyApplicable<(...RestArgs) => R>, A>)
```

As with any recursive solution, we'd need a base case. In this case, it would simply be a 0-ary or 1-ary function. In which cases we can simply return the original function signature, as there is only 1 way to call them as discussed previously.

In psudo-TypeScript, the whole solution looks something like:

```ts
// not real TypeScript, skipping argument names for simplicity
type PartiallyApplicable<F extends (...any[]) => any> =
  F extends (...infer Args) => infer ReturnV
    ? Args extends [] | [any] // base case
      ? F // there is only 1 way to call it
      : Args extends [infer A, ...infer RestArgs] // recursive case
      ? ((A) => PartiallyApplicable<(...RestArgs) => ReturnV>)
	      & PrependArg<PartiallyApplicableUnion<(...RestArgs) => ReturnV>, A>
      : never
    : never;
```

## Implementing in TypeScript

In the previous section, we've come up with a recursive definition of `PartiallyApplicable<F>` using some psudo-TypeScript.

In this section, we are going to explore how we can implement it in TypeScript.

Let's start with `PrependArg`. It is quite simple:

```ts
type PrependArg<F extends (...args: any[]) => any, X> = F extends (
  ...args: infer A
) => infer R
  ? (x: X, ...args: A) => R
  : never;
```

This implementation works for union of functions. So `PrependArg<() => number | (a: string) => void, boolean>` would become `((x: boolean) => number) | (x: boolean, a: string) => void`, but not intersection of functions.

In fact, Intersection `&` is very hard to manipulate in TypeScript, if not impossible. See [here](https://www.typescriptlang.org/play?#code/C4TwDgpgBACgThSA7AJgQTgcwDwDEoQAewEqAzlABQB0tAhlmQFxR1IgDaAugJRQC8APlbsANFAAaw-lHxES5KgCgoUWtQaZmUAJZIAZhDhQ0SvkN0GjUAEoqoAfiqEWE8es3a054XdUskCAA3IwBuJSUAekioJAB7YB0AY2gACziAdygAAwBJbKgAGziyCApgVOg4kLhiuhQRBoAjCCS4gFtoShcoMmA4PUxxJoCAV3aWuHEksYmjH1jxyaVQSChcgVgEZHQsbHtKSjpZyYWkJfmoADIqShHFuamoGYfTgWFzx55Rez6BpEwSkE4WirDAOwgDTacAQSWAhRAUGAcQIdCSqSg+lGSDhOjiSBYhx6f0G4mOr0uFk+bwAPrdif1SVB7tSjNMTpSPhc4DwVuBoABVTbwRCkXY4A5HDk894UmV0w4s7nsuVnbnfX6MgFA0JAA) for an attempt to do so.

Since union types are much easier to manipulate, we can instead generate a union of all overload functions, then use `UnionToIntersection<U>`, a magical utility type, to turn `A | B` into `A & B`. `UnionToIntersection<U>`  is too "trivial", so I won't dive into the details of it. Instead, I'll leave it as an exercise for you. 🤣 If you are interested, you can read more [here](https://github.com/type-challenges/type-challenges/issues/122).

```ts
type UnionToIntersection<U> = (
  U extends any ? (arg: U) => any : never
) extends (arg: infer I) => void
  ? I
  : never;
```
With this utility, our aim now is to define `PartiallyApplicableUnion<F>` which generates a union of all overload functions for a partially applicable `F`. We have roughly defined this using psudo-TypeScript previously, so let's translate that into TypeScript and have everything combined.

```ts
type PrependArg<F extends (...args: any[]) => any, X> = F extends (
  ...args: infer A
) => infer R
  ? (x: X, ...args: A) => R
  : never;
  
type PartiallyApplicableUnion<F extends (...payload: any[]) => any> =
  F extends (...args: infer Args) => infer ReturnV
    ? Args extends [] | [any]
      ? F
      : Args extends [infer Arg0, ...infer ArgRest]
      ?
          | ((
              arg0: Arg0
            ) => PartiallyApplicable<(...argRest: ArgRest) => ReturnV>)
          | PrependArg<
              PartiallyApplicableUnion<(...argRest: ArgRest) => ReturnV>,
              Arg0
            >
      : never
    : never;
```

Now all we have to do is:
```ts
type UnionToIntersection<U> = (
  U extends any ? (arg: U) => any : never
) extends (arg: infer I) => void
  ? I
  : never;

type PartiallyApplicable<F extends (...payload: any[]) => any> =
  UnionToIntersection<PartiallyApplicableUnion<F>>;

/* Correctly yields 4 call paths to get to the result: 
 * ((x: number, x: string, c: boolean) => number) 
 *     & ((x: number, arg0: string) => (c: boolean) => number)
 *     & ((arg0: number) => (
 *         (x: string, c: boolean) => number)
 *             & ((arg0: string) => (c: boolean) => number)
 *     )
 */
type F = PartiallyApplicable<(a: number, b: string, c: boolean) => number>
```

You can see the working code [here](https://www.typescriptlang.org/play?#code/C4TwDgpgBACgThSA7AJgQTgcwDwDEoQAewEqAzlABQB0tAhlmQFxR1IgDaAugJRQC8APlbsANFAAaw-lHxES5KgCgoUWtQaZmUAJZIAZhDhQ0SvkN0GjUAEoqoAfiqEWE8es3a054XdUskCAA3IwBueyVQSCgAVSQdAHskABUEgEkkEjgyCABjYESkbBjpZVUYgmJSFAo2EEcqTRYYnxF6gOCjM0qFGsasFj1DYzTWoISdFHsnNPsOkLhw1UjwaBgGAroAGy2QNDAwLZ1cugAjLYg4wrwe6ooaWjA6EC2EuhQWOu5Wuul7OSqigeGkYgysxgwWlaQ2sNggwAArnAkAA1eyqJyQijyO5QbhQAA+eLqXHRqgauDJ-hMjFuig4MIhWAADO5aIyaZg4WRgKTyeSHFT+YSqJQhcLyZpmSxIczxcLWus4JsdnsDkcTucINhgZpucAZVh9a04YjkSjBDx5aoifBENVIdhrcKlSrdvtDsczhcrkkdR4jRAeYauUHgCb4UjUYJRM7+bK41BBEL5l1+anFkoyStoq6dNt3eqvVqbjigeoni83h82t8BMJfgJ7L6UulMkYcvlrnmC2rPZqffE-bhBIJwkoAPQAKigAGEEnAEPldlAQDoIFsaiwVDPKJQXFAkAiALanIziA88uB6TDiXIsU4JBIXNitI+nox8HfCgBkooP75nnA4hSiwV43q0lD3lAj7PhAr71oeJ5AVaUAzvyf57qBSEfnAkHfhKqj7mBwDXkgt5QNBsEvkgb7IZ+BGERhorYeB5GQVRT40XRuGoeh5J8ROObQPgMg9qqHoat62qUHQAT0cBMEkWRFGcXBCEWIBRiCEAA).

## Calling the Partially Applicable Function with No Arguments

To extend our implementation to support calls with no arguments, it's pretty simple. Our call tree for a 4-ary partially applicable function will become:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/749hnzvhl8rlng6fsfwk.png)

Basically calling the partially applicable function with `( )` should just simply return the same function. So we can simply add one more case to our recursive case and add support for 1-ary functions:

```ts
type PartiallyApplicableUnion<F extends (...payload: any[]) => any> =
  F extends (...args: infer Args) => infer ReturnV
    ? Args extends []
      ? F
      : Args extends [any]
      ? F | (() => PartiallyApplicable<F>) // allow calls with no arguments
      : Args extends [infer Arg0, ...infer ArgRest]
      ?
          | (() => PartiallyApplicable<F>) // allow calls with no arguments
          | ((
              arg0: Arg0
            ) => PartiallyApplicable<(...argRest: ArgRest) => ReturnV>)
          | PrependArg<
              PartiallyApplicableUnion<(...argRest: ArgRest) => ReturnV>,
              Arg0
            >
      : never
    : never;
```

Note that an 1-ary partially applicable function will now have 2 ways to call. 

You can see the working version [here](https://www.typescriptlang.org/play?#code/C4TwDgpgBACgThSA7AJgQTgcwDwDEoQAewEqAzlABQB0tAhlmQFxR1IgDaAugJRQC8APlbsANFAAaw-lHxES5KgCgoUWtQaZmUAJZIAZhDhQ0SvkN0GjUAEoqoAfiqEWE8es3a054XdUskCAA3IwBuJSVQSCgAVSQdAHskABUEgEkkEjgyCABjYESkbBjpZVUYgmJSFAo2EEcqTRYYnxF6gOCjM0qFGsasFj1DYzTWoISdFHsnNPsOkLhwyPBoGAYCugAbTZA0MDBNnVy6ACNNiDjCvB7qihpaMDoQTYS6FBY67la66Xs5KsU9w0jEGVmMGC0rSG1hsEGAAFc4EgAGr2VROCEUeS3KDcNGqBq4fH+EyMG6KDh1LjEwlQAA+VEorTWcA2212+0OxzOEDwgj4AHoBaxtgkAO5QY7bChinTAAAWUCQCVYWHhAFtSMAyDSWJjyX0ONDwVgAAzuWjG0mYWFkYDUgkEhw0x0MyhMgTCFlsnZ7A5HU7nPmC4VbF4SqWbGVyxXK1WYDVanWOlP0xku1Px016s0Zx3M9Y6MMc-3coNAzS24A5m0QO2tWEIpHI-l5tPwRDVCHYNuO71F9l+rmBi7xJLYCtYKs1qsNuGIlGCUS9gkQ00rwS6pWdOD4+ZhCJRVaF4tDgM867YwHqR7PV7vNpfT1tX7lMcpdKZIw5fJXfunzlz3OS5x1wQRBCWFA8k2BhoFyJI7SgfQWH-QdALLXlKDoAINROIxxBOFg7TgPRMHEXIWBOBIEnONhxAfJBcKMVpGPVPC4AgpQhSgEAdAgTY+gAA1Y9jBKUeCkEQpABCQpk5J4SgAGYFJUygAHIyDU8RgDgeEIFU1SABYeCUIA).

## Extended: Preserving argument names

Our current implementation works totally fine. But there is one thing that keeps it away from being perfect. That is, the argument names are not kept in the overload functions.

This is due to the way we infer our arguments. When we do `F extends (x: infer X) => any`, we would lose the argument name for `X`. 

In TypeScript, argument names are basically stored in the label of array. So when you do `Parameters<(a: number, b: number) => any>`, you will get `[a: number, b: number]`. However, these label can't be easily retrieved. 

Instead of retreiving the labels, what we could do is to avoid using `infer`, but keep track of the list at all times. Instead of `[infer X, ...infer Rest]`, we would somehow retrieve the `X` as a list. We will also edit `PrependArg<F, X>` to take in `X` as an array too, so the label is passed down. Here is a way to do it:

```ts
// basically dropping the last element until we have exactly 1 element, this keeps the label of the array
type GetFirstParam<Params extends any[]> = Params extends [any]
  ? Params
  : Params extends [...infer X, any]
  ? GetFirstParam<X>
  : never;

type GetRestParam<Params extends any[]> = Params extends [any, ...infer Rest]
  ? Rest
  : [];

type PrependArg<
  F extends (...args: any[]) => any,
  X extends [any] // expect an array so the label is preserved
> = F extends (...args: infer Args) => infer ReturnType
  ? (...rest: [...X, ...Args]) => ReturnType
  : never;

type PartiallyApplicableUnion<F extends (...payload: any[]) => any> =
  F extends (...args: infer Args) => infer ReturnV
    ? Args extends []
      ? F
      : Args extends [any]
      ? F | (() => PartiallyApplicable<F>)
      :
          | (() => PartiallyApplicable<F>)
          | ((
              ...args: GetFirstParam<Parameters<F>>
            ) => PartiallyApplicable<
              (...argRest: GetRestParam<Parameters<F>>) => ReturnV
            >)
          | PrependArg<
              PartiallyApplicableUnion<
                (...argRest: GetRestParam<Parameters<F>>) => ReturnV
              >,
              GetFirstParam<Parameters<F>>
            >
    : never;

type UnionToIntersection<U> = (
  U extends any ? (arg: U) => any : never
) extends (arg: infer I) => void
  ? I
  : never;

type PartiallyApplicable<F extends (...payload: any[]) => any> =
  UnionToIntersection<PartiallyApplicableUnion<F>>;

// Example:

// notice how the argument names are now preserved
declare const f: PartiallyApplicable<
  (a: number, b: string, c: boolean, d: Date) => string
>;

// all evaluates to string
f(0, 'a', true, new Date());
f(0)('a', true, new Date());
f(0, 'a', true)(new Date());
f()()(3)()('a', true)()()(new Date());
```

You can find the fully working version [here](https://www.typescriptlang.org/play?noUncheckedIndexedAccess=true&pretty=true&noErrorTruncation=true#code/PTAECMEMGcEsGNIBskE9QBMBOB7ADnrAHYDmoALgBYCmoSM5o1S1AttUYwK6exKgB3WpUgA3WtQAekeOTSgAjExbtOAGgqVY0UAGtq1PDqq164ZqBwAzTbUhYskVAChyqPLQDi1cgDFYWNDkAAr2kKwAPKGOrDpS5BwYOpBEqADaALoAfKAAvKDR4XGSCURJoGkpqBnOoKAA-AVhsbWgAFxNMcWl5WkAdAPEVtRYoAAaGlU1dY3efgFBhZFjWa0dRNTiWADczq7uXj4AStSLzVHN3YnJqZk5+UtXZTqVqRoDfUMjoCdB0w0-U7kNYVDK7fYeApYQyJACCWBIEVaviYJWuoAAFB97CRoB0qpkAJR5HJVNStMaonovKbOe6gFHxdFYgY4vGgL6jeG44m5HKcwHkLhYIgAFQOrUaLL60KCHX6AwmoA+3OgGV5OROQpF4o8II2W3Bbkh0XIsGQaFhBCQCEg4BYAFUiLAcEQIoy0c9MR88E4kDhIBh8bd1STQFV7siqczsQj2QLVRqOURhqMtcKiAA1VozUCq6NezI5uoA3zFuodfNMwu0kslxoogA+mIxSdN5pQqCteBtiHt1HdWUJ5faI7rzYxrbD7YtXettv7g+HdZXoAnGLHq+VrLjHTm-kCIXOSx8I2gg9WW5XbfsZtn3d7dpYSKvq+lON+5D3xyBSwuMVPQILyTdMRWzV8SyHTc6mbYJoQ8MpuRfCCSxnTsHwXR1nVdZCUJLd8EU-b9yE-P8TwSIDfCyIcw1ArNoJXLJyTwup9wWI8Yn-cJAPPKjLxQ-iK1AA0RiNA5QCdF0xRwABJTgz2oWQpIiB16Q3OoHQLcoqgBDEcQ6B0kx09ZNhGZxiWrco9IRDoBRkpNRBwWAMElUAZP1Uydj2Y1aDQy15z7Z8PWpb0Bl9VB-UDYN0lDPlw1SSMNOw6S5Io6BFLNHC-LnHtMOoSScL48EQFAABRaRWB7ag2j2EqiBwM14GEHABFscMES4VRGCIbjkmhYSWtAPBZRGcQXIwRT6H6+BXSCUArA6bKMMCgdWj09ZOvMLANHADogiwYgSA0eAOnAHAcBYFINCDUAABFIASJN9sOulirAC0mFEZAuAe04KBwUBntIZwrAxAAGDQAHJIEhjRyCwLhqA0DZWvuhJW0JXZQbBwkMWh2GKARpHhOoVHfoxrHwahmG4aJ3GUbu8nCUxkHWzZgBmXHcfx2nEa5tmGbR6gKecIA).

## Limitations

This implementation has a few limitations and will not work for the following cases:

- Functions with optional arguments
- Functions with generics
- Functions with variable argument length

## Conclusion

In this article we have looked at how we can type partially applicable functions in TypeScript. We went through the patterns of partially applicable N-ary functions and spotted the recursive structure within it. 

You might be wondering, why did I spend the time trying to solve this? Partial function application isn't that useful anyways. While it is true, I started looking into typing partially applicable functions because all functions in my favourite utility library [Ramda](https://ramdajs.com) are partially applicable. 

However, in the type definitions, these partially applicable functions are explicitly typed with overflows ([example](https://github.com/ramda/types/blob/develop/types/zip.d.ts)). So the types end up being quite noisy and complicated over time. So I decided to take on this challenge of typing any N-ary functions to be partially applicable.

I hope you enjoyed reading this article. And if you have made it all the way here, congratulations and thank you for your time. I hope you've taken away somethings useful from this article. Please feel free to share your thoughts, or suggest any improvements you can think of. I'd be excited to hear from you.


[// graph for 4-ary PA Function]: (https://gist.github.com/ycmjason/08fc81b130de5136f3021a6f7526fca6)