Reputation: 11
Below is a typescript curry function definition from a medium.com article that's pretty neat. I was wondering if it would be at all possible to create a typescript curry function like this that allows for a function with generics. Or at least a way to create a curry function where you can more or less specify a custom return type.
E.g.
const example_curry = curry(<T>(name: string, int: number): Promise<T> => {...});
const named_curry = example_curry('name');
const curry_result = named_curry<string>(5);
/* eslint-disable @typescript-eslint/no-explicit-any */
// curry utilty type from https://medium.com/codex/currying-in-typescript-ca5226c85b85
type PartialTuple<
TUPLE extends any[],
EXTRACTED extends any[] = []
> = TUPLE extends [infer NEXT_PARAM, ...(infer REMAINING)]
? PartialTuple<REMAINING, [...EXTRACTED, NEXT_PARAM?]>
: [...EXTRACTED, ...TUPLE];
type PartialParameters<FN extends (...args: any[]) => any> = PartialTuple<
Parameters<FN>
>;
type RemainingParameters<
PROVIDED extends any[],
EXPECTED extends any[]
> = EXPECTED extends [infer E1, ...(infer EX)]
? PROVIDED extends [infer P1, ...(infer PX)]
? P1 extends E1
? RemainingParameters<PX, EX>
: never
: EXPECTED
: [];
type CurriedFunction<
PROVIDED extends any[],
FN extends (...args: any[]) => any
> = <
NEW_ARGS extends PartialTuple<RemainingParameters<PROVIDED, Parameters<FN>>>
>(
...args: NEW_ARGS
) => CurriedFunctionOrReturnValue<[...PROVIDED, ...NEW_ARGS], FN>;
type CurriedFunctionOrReturnValue<
PROVIDED extends any[],
FN extends (...args: any[]) => any
> = RemainingParameters<PROVIDED, Parameters<FN>> extends [any, ...any[]]
? CurriedFunction<PROVIDED, FN>
: ReturnType<FN>;
export default function curry<
FN extends (...args: any[]) => any,
STARTING_ARGS extends PartialParameters<FN>
>(
targetFn: FN,
...existingArgs: STARTING_ARGS
): CurriedFunction<STARTING_ARGS, FN> {
return function(...args) {
const totalArgs = [...existingArgs, ...args];
if (totalArgs.length >= targetFn.length) {
return targetFn(...totalArgs);
}
return curry(targetFn, ...(totalArgs as PartialParameters<FN>));
};
}
I've tried rewrites of the definition with a generic value for the return, or having curry return something like function<T>
, but in the end I was just guessing and checking as I don't really know enough about the underlying TS machinery to more intelligently tackle this. Thanks for your time!
Upvotes: 0
Views: 42
Reputation: 572
To be honest I did not completely read the question but I guess you want to curry a function and preserve its generics.
Here is what you can do:
type CurryOverload = {
<A, R>(cb: (p1: A) => R): (p1: A) => R;
<A, B, R>(cb: (p1: A, p2: B) => R): (p1: A) => (p2: B) => R;
<A, B, C, R>(cb: (p1: A, p2: B, p3: C) => R): (p1: A) => (p2: B) => (p3: C) => R;
// todo: add more later
};
const curry: CurryOverload = (() => {}) as any;
const result = curry(<N>(name: N, age: number) => name); // => const result: <N>(p1: N) => (p2: number) => N
If you want a custom return type then you can introduce a generic to the overload.
Upvotes: 0