An illustration from the release notes of TypeScript 4 demonstrates the use of variadic tuple types to eliminate multiple overload definitions. It seems feasible to type the pipe
function for any number of arguments.
type F<P, R> = (p: P) => R
type Pipe2<T1, T2, R> = [F<T1, T2>, F<T2, R>]
type Pipe3<T1, T2, T3, R> = [F<T1, T2>, ...Pipe2<T2, T3, R>]
type Pipe4<T1, T2, T3, T4, R> = [F<T1, T2>, ...Pipe3<T2, T3, T4, R>]
function pipe<T1, R>(f1: F<T1, R>): F<T1, R>
function pipe<T1, T2, R>(...fns: Pipe2<T1, T2, R>): F<T1, R>
function pipe<T1, T2, T3, R>(...fns: Pipe3<T1, T2, T3, R>): F<T1, R>
function pipe<T1, T2, T3, T4, R>(...fns: Pipe4<T1, T2, T3, T4, R>): F<T1, R>
function pipe(...fns) {
return x => fns.reduce((res, f) => f(res), x)
}
A starting point could be
function pipe<Fns>(...fns: PipeArgs<Fns>): PipeReturn<Fns>
function pipe(...fns) {
return x => fns.reduce((res, f) => f(res), x)
}
We still need to define the helper types PipeArgs<Fns>
and PipeReturn<Fns>
. Is there another approach to achieve this?
Edit: With the current TypeScript version (4.1.2), achieving this seems more challenging. The rest parameter types in pipe
need to be inferred with a specific structure. Here is an approach that includes a working PipeReturn<Fns>
type.
(... code snippet ...)
Before presenting the various pipe
signatures that didn't work as expected, let's explore some tests and examples to understand their behavior.
(... code snippet ...)
The following pipe
signatures and the corresponding tests/examples that failed to meet expectations are highlighted.
(... code snippet ...)
By adding Fns &
to the previous approach, the previous error was fixed but a new expected error did not occur.
(... code snippet ...)
Another idea is to enforce in the return type that Fns
has the expected structure, but this definition also has an error.
(... code snippet ...)
Edit 2: Additionally, the ts-toolbelt library offers several type definitions to type your pipe
function for up to 10 arguments, although not for an unlimited number of arguments.