Search code examples
typescriptfunctiongenericstypescript-genericscurrying

Currying with bind function and to correctly type with generic in TypeScript


I have written a sort function and would like to bind a compare function to it. Unfortunately, the TypeScript compiler warns me for unknown type for the compare function.

I have tried search for SO: Bind function to function and search in TS Handbook: bind. However, I cannot find any related issue.

And tried to follow the type in the declaration file:

    /**
     * For a given function, creates a bound function that has the same body as the original function.
     * The this object of the bound function is associated with the specified object, and has the specified initial parameters.
     * @param thisArg The object to be used as the this object.
     * @param args Arguments to bind to the parameters of the function.
     */
    bind<T>(this: T, thisArg: ThisParameterType<T>): OmitThisParameter<T>;
    bind<T, A0, A extends any[], R>(this: (this: T, arg0: A0, ...args: A) => R, thisArg: T, arg0: A0): (...args: A) => R;
    bind<T, A0, A1, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1): (...args: A) => R;
    bind<T, A0, A1, A2, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2): (...args: A) => R;
    bind<T, A0, A1, A2, A3, A extends any[], R>(this: (this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, thisArg: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3): (...args: A) => R;
    bind<T, AX, R>(this: (this: T, ...args: AX[]) => R, thisArg: T, ...args: AX[]): (...args: AX[]) => R;
function sorting<T>(a: T, fn: (v0: T, v1: T) => number,  b: T) {
  return fn(a, b)
}

sorting<number>.bind(null, 1, (a, b) => a + b)
//               ^ Cannot find name 'bind'.(2304)

sorting.bind<null, number, (v0: number, v1: number) => number, [b: number]>(null, 1, (a, b) => a + b)
//^ Argument of type '(b: number) => unknown' is not assignable to parameter of type //'(v0: number, v1: number) => number'.
//  Type 'unknown' is not assignable to type 'number'.(2345)

TS Playground

And it still does not work. Am I missing something here? Why I can not bind arguments a and fn?


P.S.

Currently I used arrow function to curry the sort function as a work around.


Solution

  • This is actually a common problem, and so common that it is being fixed in the next minor version of TypeScript (4.7)! The solution that is being used is called an instantiation expression.

    Here's their example:

    interface Box<T> {
        value: T;
    }
    
    function makeBox<T>(value: T) {
        return { value };
    }
    

    While you could do this:

    const makeStringBox = (value: string) => makeBox(value); // makeBox<string>(value: string)
    

    It is quite wasteful, so 4.7 introduces new syntax:

    const makeStringBox = makeBox<string>; // type parameters passed without calling it!
    

    This also means you can pass it to sorting before binding it:

    // works in 4.7+ now!
    (sorting<number>).bind(null, 1, (a, b) => a + b)
    

    You can try the nightly version of TypeScript (4.8+) here with this example.