curry a function fn, with optional thisArg option for binding as the this-object.
what is currying? it allows a multi-parameter function to be transformed into a higher order function that always takes one argument, and spits out another function which also take only one argument, and so on... until all parameters have been filled, upon which the final function finally evaluates into the return value (type parameter R in this case).

note that this function relies on fn.length property to identify the number of required arguments taken by fn. this means that default valued arguments (such as c in fn: (a: number, b: number, c = 5) => number), or rest/spread arguments (such as args in fn: (a: number, b: number, ...args: number[]) => number), are not considered as required, and thus do not increment the count of fn.length.

currying is usually implemented terribly through the use of closure. example: ((arg0) => (arg1) => (arg2) => fn(arg1, arg2, arg3))()
this is bad because when you evaluate a curry with N-parameters, you also have to make N-calls (albeit it being tail-calls), instead of just one call should you have had all the parameters from the beginning. not to mention that all javascript engines famously do not perform tail-call optimizations.
but here, I've implemented currying using the bind method, which means that once all parameters are filled, the function goes through only one call (no overheads).
the same example from before would translate into: fn.bind(thisArg, arg0).bind(thisArg, arg1).bind(thisArg, arg2)() when binding is used

const abcd = (a: number, b: string, c: boolean, d: symbol): string => (String(a) + b + String(c) + String(d))
const abcd_curry = curry(abcd) // type: (arg: number) => (arg: string) => (arg: boolean) => (arg: symbol) => string
console.log(
abcd_curry(42) // type: (arg: string) => (arg: boolean) => (arg: symbol) => string
(" hello to za warudo! ") // type: (arg: boolean) => (arg: symbol) => string
(true) // type: (arg: symbol) => string
(Symbol.iterator) // return type: string
) // logs `"42 hello to za warudo! true Symbol(Symbol.iterator)"`