Open js-choi opened 2 years ago
If it's useful to anyone. The following utility function can wrap a function so that the receiver can be passed as the first argument, which currently works better for TypeScript inference.
function uncurryReceiver<Args extends any[], R, O>(
op: (...args: Args) => (receiver: R) => O
) {
return function(receiver: R, ...args: Args): O {
return op(...args)(receiver);
}
}
Example usage:
function map<V, R>(f: (v: V) => R) {
return function(arr: Array<V>): Array<R> {
return arr.map(f);
}
}
map(v => v + 1)([1, 2, 3]); // no inference, 'v' is 'unknown' ❌
const _map = uncurryReceiver(map);
_map([1, 2, 3], v => v + 1); // 'v' is inferred as 'number' ✅
Related topic: Inconsistency of Type of the operator #227 Fundamentally this is the math issue, with F# pipe, the problem will not occur.
Mm, #227 is about a different topic. This topic is merely about documenting tc39/proposal-hack-pipes#18, which is just the fact that TypeScript currently already cannot automatically infer the types of curried function calls like map(f)(x)
without a manually declared pipe
function (as above, see the definition of RxJS’s pipe
function, which manually declares its types ten times for ten arities).
So just like how TypeScript already cannot automatically infer the type of map(f)(x)
, it cannot infer the type of x |> map(f)(^)
. But it is able to infer the type of map(x, f)
, and so it would also be able to infer the type of x |> map(^, f)
. This problem isn’t something about the Hack pipe. The problem is about TypeScript and curried function calls, which has always existed (as above, see microsoft/TypeScript#15680, microsoft/TypeScript#22081, microsoft/TypeScript#25826, microsoft/TypeScript#29904, microsoft/TypeScript#30134).
Anyways, both of these comments are off topic, so I will mark them as such. ^_^
Spinning this out of tc39/proposal-hack-pipes#18 (thanks @OliverJAsh). He raised an important point: that
rx.pipe
, etc. currently work with but would not work with Hack pipes (or F# pipes?).We need to document this in the explainer in an FAQ.
To be clear, this is not due to any inherent mathematical property of Hack pipes.
This is a manifestation of a limitation of TypeScript’s current inference algorithm: it can only unify types from left to right, and it cannot unify types in expressions like
map(i => i + 1)(items)
.This is an incidental TypeScript limitation, about which @OliverJAsh has been raising issues for several years (see microsoft/TypeScript#15680, as well as the related microsoft/TypeScript#22081, microsoft/TypeScript#25826, microsoft/TypeScript#29904, microsoft/TypeScript#30134).
Indeed, this limitation is why RxJS and Ramda must use a brittle workaround for their
pipe
functions: they manually overload the arity of their pipe functions up to a finite number. For example, RxJS manually overloads the arity ofrx.pipe
ten times:As can be seen, this means that
rx.pipe
only supports automatic TypeScript inference when piping a value through less than nine functions; more than ten functions would require manual typing.Again, this is not due to any inherent mathematical property of Hack pipes. In fact, this probably would not work with F# pipes either. It is the result of not being able override the arity of a pipe operator and to specify the type of each arity, as RxJS does above (and as Ramda, etc. do too).
The amazing TypeScript team has been working hard on a new, more robust type inferencer: one that improves its type unification so that it retains free types (microsoft/TypeScript#30134). But of course this means that we can’t do
map(i => i + 1)(items)
today, and we therefore can’t doitems |> map(i => i + 1)(^)
today. (I’m sorry that Hack pipes don’t give a good answer to this now—other than to manually annotate types as needed (which TypeScript developers often already have to do) and hope for microsoft/TypeScript#30134 to land.)In any case, the explainer does not talk about this. This is a deficiency of the explainer. We need to fix this sometime.
This issue tracks the fixing of this deficiency in the explainer (lack of discussion regarding TypeScript’s current limitations). Please try to keep the issue on topic (e.g., comments about the importance of tacit programming would be off topic), and please try to follow the code of conduct (and report violations of others’ conduct that violates it to tc39-conduct-reports@googlegroups.com). Please also try to read CONTRIBUTING.md and How to Give Helpful Feedback. Thank you!