Function to build a string based on the recursion depth
Review on code.
The term &quot;ugly&quot; is subjective. For some ugly is bad practice or anti-patterns, and there are others that will argue that current standards of good practice are &quot;ugly&quot; due to inefficiencies.
I will take ugly in this case to mean inelegant in the mathematical sense of the term. Thus below is a review in terms of improving code for simplicity and efficiency.
Simplify.
There is no reason to use statement clause for undefined in the form typeof blah === &quot;undefined&quot;. Assuming that no input will equate to false apart from no arguments then if(! input) { is simpler.
However, there may be an input like f()(false), where the expected output is &quot;fofalse&quot;, which will require a more robust test for undefined. So the safe option without knowledge of possible input is if(input === undefined){ which is still simplier (and more efficient).
For each call of the function f there are only two possible return values, the string that terminates the chain of calls or the function that is used to expand the return string.
Your inner function has 4 return possibilities, which is an obvious unneeded complication.
Simplicity also means not to repeat code. The outer function and inner function have the same functionality. Thus the outer function only needs to begin a process of building a string. This can be done via a closure, so all the outer function need do is define a communicating variable and a function to hold the closure. It does not need to do any form of logic.
Efficiency
Simplicity and efficiency go hand in hand.
The inner function should only return a string or a function to do the same.
The communication between the function calls is where you have added too much overhead with the creation of objects to communicate to the outer function.
You can use a closure variable to communicate information between calls and reduce the call stack depth by not needing to call the outer function. The closure is maintained by returning the inner function in the case of an undefined input.
The result.
The result is hopefully less ugly in your eyes. For me it is far simpler and nicely elegant.


function f(str) {
    var content = "f"; 
    return (function inner(str){      
        if (str !== undefined) { return content + str }
        content += "o";
        return inner;
    })(str);          
}

console.log(f("a"));
console.log(f()("a"));
console.log(f()()("a"));



Alternative.
I did toy with the following version but for me it is not as elegant due to the forced flow unique to Javascript that will put those from other languages out of joint. Though it is simpler in terms of token count but not in complexity.


function f(str) {
    var content = "f"; 
    return (function inner(str){      
        return str ? content + str : ( content += "o", inner );
    })(str);          
}

console.log(f("a"));
console.log(f()("a"));
console.log(f()()("a"));