DEV Community

Creating a typed "compose" function in TypeScript

Matt Kane on January 02, 2019

I recently wrote a couple of small utility functions that turned into a deep exploration of TypeScript generics, as well as the new typed tuples in...
Collapse
 
skurfuerst profile image
Sebastian Kurfürst

Hey Matt,

Thanks for your article!

I am having a question I don't really understand: In my understanding, the compose function can compose functions which return different types than what they expect; i.e. when plugging the pipe together, the output type of the first function must match the input type of the second function.

So, as I understand it, if you compose n functions together, you would have n+1 types.

Is this also handled by your function above? If so, I do not understand yet how this works :)

All the best,
Sebastian

Collapse
 
ascorbic profile image
Matt Kane

Hi Sebastian,
This isn't as smart as that: it expects that they all return the same type. If be interested to see if there's a way of typing the other sort though.

Collapse
 
skurfuerst profile image
Sebastian Kurfürst

Hey Matt,

the only way I see it is via N+1 different generic types - though that is ofc. not as nice as your generic implementation.

See:

All the best, Sebastian

Thread Thread
 
stereobooster profile image
stereobooster

There is a way to simulate it, but wiht limitations github.com/reduxjs/redux/blob/686d...

Thread Thread
 
ascorbic profile image
Matt Kane

Yeah, I can't see any way that doesn't boil down to "use lots of repetitive overloads"

Collapse
 
waynevanson profile image
Wayne Van Son • Edited

I got composable to work the same as pipe: multiple initial arguments.

export function compose<R, F extends (a: R, ...b: any) => R>(
  fn1: F,
  ...fns: Array<(a: R) => R>
) {
  return fns.reduce(
    (prevFn, nextFn) => value => prevFn(nextFn(value)),
    fn1
  ) as F;
}

const a = (v: string, q: number, s: boolean) => v + q;
const b = (v: string) => v;

const c = compose(a, b, b, b, b, b, b);
Enter fullscreen mode Exit fullscreen mode

I can say that I've been wanting to find this post for at least a year!

Collapse
 
mackentoch profile image
John Doe

Thank you for this article.

But I feel like the more I practice and read about Typescript the more it seems better for OOP programming rather than functional programing (React uses so much more functional concepts than Angular for instance).

It is such a pain (maybe just because I'm too ignorant with TS?) in a React application (HOC, composition...) that I feel more productive and things stay far more simple with FlowJS (only where I need not all codebase).

Collapse
 
ascorbic profile image
Matt Kane

I don't agree here. The reason I wrote about these is precisely because they're edge cases. There's no reason that FP should be any harder than OOP in TypeScript. If you're happy with Flow, in 99% of cases the TypeScript syntax is basically identical. The main difference you'll see is just better tooling and more typings available.

Collapse
 
mackentoch profile image
John Doe

Agreeing is not important, but sharing thoughts is what make us evolve.

Thank you for taking time replying 🙏

Collapse
 
krumpet profile image
Ran Lottem

Great stuff! I played around with a ComposableOn type, to implement compose for different types:

type FunctionType = (...x: any[]) => any;
type ComposableOn<F extends FunctionType> = F extends (...x: any[]) => infer U ? (y: U) => any : never;

This enforces that a function f2 can be composed on f1 to give f2(f1(...args).

Still couldn't do anything too cool about a variadic implementation. Something like CPP's <typename... T> or any of the other suggestions in comments here would be really cool.

Collapse
 
kotarski profile image
Edward Kotarski

I wrote an NPM package for this (npmjs.com/package/ts-functionaltypes)
Which checks the function types for the entire pipe

Collapse
 
babak profile image
Babak

Hey Matt--you might find my article on creating a recursive pipe / compose of interest

dev.to/babak/introducing-the-recur...

Collapse
 
abraham profile image
Abraham Williams

Nice write up and I love the inclusion of tests!