Repository navigation
Pipe/flow/chain type support #30370
Description
Search Terms
recursive, flow, pipe, chain
Suggestion
I do not have a specific implementation in mind to support this. There is a decent existing solution by a stackoverflow user Joe Calzaretta (@jcalz), which does its best given what typescript is able to accomplish now. I have created this issue to more to specifically point out the current limitation of typescript in regard to this feature, and the current use cases. This feature request falls under the larger umbrella of recursive type inference.
At its most broad, I would like to suggest that typescript include some form of an official Chain type that developers can use.
Unanswered Questions
- should this type have an official implementation with a max recursion value?
pipe: Chain<In, Out, number> - will this design pattern cease to be relevant if the tc39 pipeline proposal is integrated into javascript?
Use Cases
There are a number of implementations for javascript pipes in common libraries.
Related Issues
Examples
a typical implementation looks like this:
function pipe<T, A>(arg1: (in: T) => A): A
function pipe<T, A, B>(arg1: (in: T) => A, arg2: (in: A) => B): (in: T) => B
function pipe(...args: any[]): (in: any) => any
const strToNumber = (str: string) => parseInt(str)
const add = (x: number) => (y: number) => x + y
const safeAdder = pipe(strToNumber, add(5))
const out: number = safeAdder('6') // returns 11The limitation of the current implementation is that if you use a typed pipe function with more arguments than the written overloads, you run lose type inference.
Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
Activity
steve-taylor commentedon Aug 23, 2020
As this needs a proposal, here’s my $0.02, using the example of a hypothetical observable library heavily inspired by RxJS.
In a regular parameter,
Ais indexed using an absolute index.In a rest parameter,
Ais indexed using an index relative to the index of the actual parameter.In the return type,
A[-1]represents the last type parameter. (A[-2]would be the 2nd last type parameter, and so on.)Note: I can’t think of a way to reference these types in the method body. It might be impossible. For that reason, my proposal is limited to method/function declaration only. It wouldn’t be value syntax for method/function implementations.
The implementation of functions and methods would be no worse than the current situation, but at least usages of these functions and methods would now be type checked to any depth without having to pump out a dozen overloads.
andykais commentedon Aug 24, 2020
I suppose if were spitballing, Ill take a stab as well. I do think thats a decent idea, but its a little hairy because the generic param
...Ahas to read the function argument indexes to figure out what its minimum length is and...restOperators: Operator<A[0], A[1]>[]doesnt actually specify that the pattern is repeating. I believe what we need is some sort of recursive structure so heres another thought:but even this doesnt really describe that the 2nd and 3rd arguments should follow the same pattern as the 1st and 2nd.
MobiusHorizons commentedon Dec 1, 2020
My vote would be to describe such types using something similar to the mathematical notation where
nis used to describe a series, which is then defined by relationships between members of that series.mdbetancourt commentedon Oct 31, 2021
i manage to get it to "work" but looks like a typescript error
profound7 commentedon Mar 26, 2022
I managed to implement a pipe function type. Instead of overloading, it uses tuple rest args and recursion.
There are 2 flavors of the pipe type. One returns a function (
Pipe), and the other has a value as the first argument and immediately executes (PipeValue).It correctly identifies the first incorrect argument.
The return type of the pipe function is correctly inferred even if there are incorrect arguments.
For compose, just switch around the inferring of A and R
smolijar commentedon May 12, 2022
Works great Munir Hussin (@profound7). I wonder, is it also possible to optionally infer the value of the first function from the composed function argument?
Azarattum commentedon Aug 26, 2022
Munir Hussin (@profound7), unfortunately this still performs worse that overloads implementations, since it can not infer types from a previous function in a chain:
This looks to be a Typescript bug, since the playground even suggests that
nshould be anumberwhen you start typing the second function. But when you hover over it, typescript saysnisany.Is there any progress to fully support chain types in Typescript? The issue is here since 2019...
Azarattum commentedon Aug 28, 2022
This is the best (shortest & fullest) solution I've made so far:
Try it on the playground!
If you can think of a better/shorter solution, please post it in this thread and tag me. Thanks.
UPDATE: an improved version
Bessonov commentedon Aug 28, 2022
Azarattum (@Azarattum) great, thanks for sharing! It's me or
xresolves toanyinstead ofnumberin the line 53?Azarattum commentedon Aug 28, 2022
Anton Bessonov (@Bessonov) this is by design. I did this intentionally to demonstrate that type inference works only for the first 7 functions. Since they are implemented manually with overloads. The rest is just type checked but not inferred. So, if you write
(x: string) => 123,on the line 53, you'll get an error. This is a typescript limitation see #50468.Azarattum commentedon Aug 28, 2022
I've made an updated version which:
Playground
Code
Examples
Azarattum commentedon Mar 9, 2023
@olmo-hake you can check out my full pipe/pipeline implementation that I’m using for my libfun library here. It supports promises and other promise-like monadic structures.
janekeilts commentedon Sep 26, 2024
I made this work with async its not clean but works well:
janekeilts commentedon Sep 26, 2024
Also I guess:
profound7 commentedon May 21, 2025
Azarattum (@Azarattum) Jaroslav Šmolík (@smolijar)
Using overloading solution by Azarattum (@Azarattum) I found that this also works
Example
It appears that the type parameters to the pipe function is what was needed to make the inference of function types to work correctly.
edit: updated pipe to accept a value at the beginning, and added flow as the curried version.