I usually curry my function manually through arrow syntax. There seems to be an issue with type inference as soon as generic type parameters come into play:
type Const<A, B> = { const: A };
const Const = <A, B>(x: A): Const<A, B> => ({ const: x });
const map = <F, A, B>(f: (_: A) => B) => (tx: Const<F, B>) => tx;
const inc = (x: number) => x + 1;
const foo = map(inc) (Const("foo")); // Const<unknown, number> instead of <string, number>
When I uncurry map
everything works as expected. How can I keep my functions curried without losing type inference?
You just need to move type parameters to the function where they are actually used as parameters.
In this case F
has no inference site in the map
, it is only used in the return type (inferring from called return type, is not something TS does, it does infer from return type in other scenarios but not this). Moving the type parameter to the function returned by map
lets the second call infer F
and produces the expected type.
type Const<A, B> = { const: A };
const Const = <A, B>(x: A): Const<A, B> => ({ const: x });
const map = <A, B>(f: (_: A) => B) => <F>(tx: Const<F, B>) => tx;
const inc = (x: number) => x + 1;
const foo = map(inc) (Const("foo"));