How to get this reduce function to return type Set<1>? (Playground)
const toSet = <const V>(acc=new Set<V>(),v:V) =>{
acc.add(v);
return acc;
}
const set = toSet(undefined,1); // Set<1> correct
const reduce =
<NextReducer extends (acc:any,value:any)=>any>(nextReducer:NextReducer) =>
(acc: unknown, value: unknown) =>
nextReducer(acc, value);
const reduced = reduce(toSet)(undefined,1); // any (should be Set<1>)
TypeScript's support for higher order type inference from generic functions only works in specific limited circumstances, as implemented in microsoft/TypeScript#30215. One requirement is that the higher order function needs to be generic in the argument and return types of its input functions separately, and not generic in the entire input function types.
Meaning you cannot write <F extends (acc:any,value:any)=>any>(nextReducer: F)=>⋯
as you've written in your question. Instead you need to write something like <A, V, R>(nextReducer: (acc: A, value: V) => R)=>⋯
. That gives you the following version of reduce
:
const reduce =
<A, V, R>(nextReducer: (acc: A, v: V) => R) =>
(acc: A, value: V) => nextReducer(acc, value);
The resulting type of reduce
is <A, V, R>(f: (a: A, v: V) => R) => (a: A, v: V) => R
.
This is safer than your version, since the returned function should not have parameters like (acc: unknown, value: unknown)
, as nextReducer
might not accept unknown
arguments. (e.g., nothing stopped someone from calling reduce(toSet)("oops", "darn")
and getting a runtime error on "oops".add("darn")
)
And, more importantly for your question, this allows the higher order type inference to work:
const reduceToSet = reduce(toSet);
// const reduceToSet: <const V>(acc: Set<V> | undefined, value: V) => Set<V>
You can see that reduce(toSet)
preserves the generics from toSet
. And thus you get the desired behavior:
const reduced = reduce(toSet)(undefined, 1);
// const reduce: Set<1>
So that's the answer to the question as asked.
Note that if you're actually planning to try to turn reduce
into a real reduction operation which operates on a recursive data structure like an array or tree, you'll probably find that this is more complicated and preservation/propagation of generics might be more difficult or even impossible. But that's outside the scope of the question as asked.