1

I am working with curried functions in TypeScript, and I want to figure out a way to automatically detect if a function is curried and, if it is, decurry it into a non-curried function.

Problem:

  • A curried function is a function that takes arguments one at a time and returns a new function for each argument.
  • I need a method to detect whether a function is curried (i.e., returns a function when called with one argument), and if it is, I want to decurry it into a function that can accept all arguments at once.

For example, given the following curried function:

FROM

const curriedAdd = (a: number) => (b: number) => (c: number) => a + b + c;

TO

const add = (a: number, b: number, c: number) => a + b + c;

I’ve experimented with some basic heuristics to detect currying, such as calling the function with one argument and checking if the result is another function. However, I’m unsure how to automatically decurry a function at runtime. Specifically, I’m struggling with how to handle functions that take multiple arguments or have a more complex curried structure. I've tried seeking help from AI also but unfortunately it keeps responding with total non-sense that doesn't work.

  • I need this solution to work for simple curried functions as well as more complex ones (if possible).
  • I am aware of currying and decurrying techniques, but I’m specifically looking for a runtime solution that works in TypeScript.
5
  • 2
    This sort of thing is really fiddly. Strange things can happen with edge cases, so I'm hoping you'll edit to show a range of use cases you care about (rather than just alluding to them by saying"more complex ones"). With what you've written here you could do this approach. Does that meet your needs (please test thoroughly before answering)? If so I'll write an answer explaining; if not, what's missing? Commented Feb 28 at 21:45
  • 1
    This is not generally possible since you cannot detect the type of a function at runtime. Commented Feb 28 at 23:13
  • Right, the best you can do is assume that you've called it properly; if you look at my playground link TypeScript is "handling" the correctness (except, who knows, edge cases?), no runtime detection at all. Feels brittle to me, so I'd love for OP to edit with a range of use cases to make sure it's possible. Commented Feb 28 at 23:24
  • if my answer solved your problem don't hesitate to upvote (the gray checkbox), that's how stackoverflow works, thanks (stackoverflow.com/help/someone-answers) Commented Mar 1 at 10:05
  • It's possible OP has abandoned this? They wrote an answer that contained a link to a playground that I -think- had use cases, but they should really come back here and edit those use cases here, as plaintext, and we can proceed. Commented Mar 1 at 14:13

1 Answer 1

1

You could use some recursive type and runtime chain calling, but if you want to avoid the chain calling and make inline code I'm afraid you need some code parsing which is very brittle, for example a parameter could be mutated before passing to the next function. So you need to parse function parameter lists and bodies, which is crazy. Maybe acorn could help.

A fast solution to your problem could be:

Playground

const curriedAdd = (a: number) => (b: number) => (c: number, d: number) => a + b + c + d;

type Fn = (...args: any) => any;

type Decurry<F extends Fn> = 
    F extends (...args: infer P) => infer R ? R extends (...args: infer A) => infer B ? Decurry<(...args: [...P, ...A]) => B> : F : never;

type decurried = Decurry<typeof curriedAdd>;

function decurry<F extends Fn>(fn: F): Decurry<F> {
    return ((...args: any[]) => {
        let result = fn;
        while(typeof (result = result(...args)) === 'function'){
            args.splice(0, fn.length);
        }
        return result;
    }) as any;
}

const decurried = decurry(curriedAdd);

const sum = decurried(1,1,1,1);
console.log(sum);
Sign up to request clarification or add additional context in comments.

Comments

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.