4

I have the following definition for a curried function in TypeScript:

interface Curried2<A, B, Z> {
    (_0: A): (_0: B) => Z;
    (_0: A, _1: B): Z;
}

I have the following function that should accept a function (curried or not):

function apply<T, U>(f: (_0: T) => U, x: T): U {
    return f(x);
}

Now, given

let curried: Curried2<number, boolean, string> = ...

the following works (as expected):

apply<number, (_0: boolean) => string>(curried, 4);

But TypeScript cannot infer the type on its own:

apply(curried, 4);

(Even though there is only one overload for () which takes a single value.) It complains:

Argument of type 'Curried2<number, boolean, string>' is not assignable to parameter of type '(_0: number) => string' ...
It has correctly inferred T, but inferred U to be string. Why is this? What can I do to make type inference work for me in this case (as explicitly specifying T and U is too verbose for my taste)?

Thanks in advance!

2 Answers 2

1

The following now works:

interface F1<A, Z> {
    (_0: A): Z;
}

interface F2<A, B, Z> extends F1<A, F1<B, Z>> {
    (_0: A, _1: B): Z;
}

function apply<T, U>(f: F1<T, U>, x: T): U {
    return f(x);
}

function apply2<T, U, V>(f: F2<T, U, V>, x: T, y: U): V {
    return f(x, y);
}

let curried: F2<number, boolean, string> = null;

const x: F1<boolean, string> = apply(curried, 4);
const y: string = apply2(curried, 4, false);
Sign up to request clarification or add additional context in comments.

Comments

0

I could try this approach:

type Curried<T1, T2, T3> = (x: T1) => (y: T2) => T3;

function apply<T, U>(f: (value: T) => U, x: T): U {
    return f(x);
}

//simple demo
var someFunc: Curried<string, number, [string, number]> = x => y => [x, y];
var curriedSomeFunc = apply(someFunc, "string here");
var result1 = curriedSomeFunc(0); //will be ["string here", 0]
var result2 = curriedSomeFunc(1); //will be ["string here", 1]

Another try. The syntax is correct but no safety if you pass not a correct function for currying:

interface Curried<T1, T2, T3> {
    (x: T1, y: T2): T3;
    (x: T1): (y: T2) => T3
}

let f1 = <Curried<string, number, [string, number]>>
    ((x: string, y: number) => [x, 1]);
let f2 = <Curried<string, number, [string, number]>>
    ((x: string) => (y: number) => [x, y]);

function apply<T, V>(f: (x: T) => V, x: T): V {
    return f(x);
}

let curriedF1 = apply(f1, "42"); //Won't work (no function to curry) but type inference is OK
let curriedF2 = apply(f2, "11"); //Will work and type inference is also OK 

4 Comments

In this case I would lose the ability to use the function as a regular uncurried function, so that won't work, unfortunately :(
That's it! The only difference between your example (in which type inference works) and mine (in which it doesn't) is the order of the overloaded methods of Curried. Any idea why this is?
Now the following won't work anymore: function applyTwo<S, T, V>(f: (x: S, y: T) => V, x: S, y: T): V { return f(x, y); }; applyTwo(f1, "23", 23); What, do you think, is the reason for this?
I don't think it's a good practice to use "overloading" for a function interface. It seems more like a typescript design flaw than a real feature. I would prefer to use currying technique via multiple arrow expressions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.