Generic curry function with n arguments in typescript

I can create a general currying function for functions with a given number of arguments. IE)

function curry2<T1,T2,R>(func:(arg1:T1, arg2:T2) => R, param2: T2):(arg:T1) => R{
    return (param1:T1) => func(param1, param2);
};

However, I cannot find a way (typafe) to implement a common curry function for a function with any number of arguments. In another language, I would call all my currying functions (that is: curry1, curry2, curry3, etc.) The same thing (curry), and then overloading the functions performed the correct curry implementation. However, typescript does not allow function overloading like this.

It's not too boring to write curry2 / curry1 / curry3 everywhere, and not a single curry interface, but if there is a way to do this, I would be happy to know how to do it.

+4
source share
3 answers

Not too annoying to write curry2 / curry1 / curry3 everywhere, not a single curry interface,

You can overload (doc https://basarat.gitbooks.io/typescript/content/docs/types/functions.html )

More details

Something where you started:

function curry<T1,T2,R>(func:(arg1:T1, arg2:T2) => R, param2: T2):(arg:T1) => R;
function curry<T1,T2,T3,R>(func:(arg1:T1, arg2:T2, arg3: T3) => R, param2: T2):(arg:T1) => R;
function curry(){
    // Implement
    return undefined;
};
+1
source

The basarat example is not bad, but the second overload does not seem correct. This is what I came up with when writing a type signature for the curry function:

interface curry {
    <T1, T2, R>(func: (p1: T1, p2: T2) => R): (p: T1) => (p: T2) => R;
    <T1, T2, T3, R>(func: (p1: T1, p2: T2, p3: T3) => R): (p: T1) => (p: T2) => (p: T3) => R;
    <T1, T2, T3, T4, R>(func: (p1: T1, p2: T2, p3: T3, p4: T4) => R): (p: T1) => (p: T2) => (p: T3) => (p: T4) => R;
    <T1, T2, T3, T4, T5, R>(func: (p1: T1, p2: T2, p3: T3, p4: T4, p5: T5) => R): (p: T1) => (p: T2) => (p: T3) => (p: T4) => (p: T5) => R;
}

And of course, you can repeat it with a lot of parameters until you reach a safe depth :)

// the actual curry function implementation ommited
var makeCurry: curry = <any> null;

// example function that we would like to curry, with two parameters
var getInfo = (age: number, name: string) => {
    return `${name} is ${age} years old`;
}

// the previous function curried
var getInfoCurried = makeCurry<number, string, string>(getInfo);

var info = getInfoCurried(26)('Gergo');
0
source
  • curry.js:

    module.exports = (fn) => {
      const f = (fn, length, arr) =>
        (...args) =>
          args.length < length ?
            f(fn, length - args.length, arr.concat(args)) :
            fn(...arr.concat(args));
      return f(fn, fn.length, []);
    };
    
  • curry.d.ts:

    declare module "curry" {
      export interface curry1<T1, R> {
        (): curry1<T1, R>;
        (t1: T1): R;
      }
    
      export interface curry2<T1, T2, R> {
        (): curry2<T1, T2, R>;
        (t1: T1): curry1<T2, R>;
        (t1: T1, t2: T2): R;
      }
    
      export interface curry3<T1, T2, T3, R> {
        (): curry3<T1, T2, T3, R>;
        (t1: T1): curry2<T2, T3, R>;
        (t1: T1, t2: T2): curry1<T3, R>;
        (t1: T1, t2: T2, t3: T3): R;
      }
    
      export interface curry4<T1, T2, T3, T4, R> {
        (): curry4<T1, T2, T3, T4, R>;
        (t1: T1): curry3<T2, T3, T4, R>;
        (t1: T1, t2: T2): curry2<T3, T4, R>;
        (t1: T1, t2: T2, t3: T3): curry1<T4, R>;
        (t1: T1, t2: T2, t3: T3, t4: T4): R;
      }
    
      export function curry<T1, R>(fn: (t1: T1) => R): curry1<T1, R>;
      export function curry<T1, T2, R>(fn: (t1: T1, t2: T2) => R): curry2<T1, T2, R>;
      export function curry<T1, T2, T3, R>(fn: (t1: T1, t2: T2, t3: T3) => R): curry3<T1, T2, T3, R>;
      export function curry<T1, T2, T3, T4, R>(fn: (t1: T1, t2: T2, t3: T3, t4: T4) => R): curry4<T1, T2, T3, T4, R>;
      // etc...
    
      export = curry;
    }
    

tsc -v = 2.2.2

0
source

Source: https://habr.com/ru/post/1654246/


All Articles