Reputation: 21
I have a bulk of generated functions with different amount and types of arguments. Like this:
function f(a: string, b: number): number;
function f1(a: number): string;
function f2(a: boolean): void;
// etc...
I need to wrap them like this:
function myF(a: MaybeRef<string>, b: MaybeRef<number>): number;
function myF1(a: MaybeRef<number>): string;
function myF2(a: MaybeRef<boolean>): void;
// etc...
How can I achieve this?
I've tried something like:
type MaybeRefParameters<T extends (...args: any) => any> = T extends (
...args: infer P
) => any
? MaybeRef<P> // <-- It wraps all arguments, not individual
: never
function my<F extends (...data: any) => any>(f: F): ReturnType<F> {
return (...args: MaybeRefParameters<F>) => f(args.map(unref))
}
const myF = my(f)
const myF1 = my(f1)
const myF2 = my(f2)
But it does not work.
Upvotes: 1
Views: 543
Reputation: 329523
You can use mapped tuples to turn the parameter tuple type of the passed in function to a new tuple type where each element is wrapped with MaybeRef<>
. I would suggest using the parameter tuple type A
and the function return type R
as separate generic type parameters instead of using the whole function type F
and then fiddling with the Parameters
or ReturnType
utility type:
function my<A extends any[], R>(f: (...a: A) => R) {
return (...args: { [I in keyof A]: MaybeRef<A[I]> }) => f(...args.map(unref) as any)
}
Note that the compiler will definitely not be able to understand that args.map(unref)
will do any kind of tuple type mapping which is different for each element. Without higher kinded types as requested in microsoft/TypeScript#1213, you can't even represent that sort of transformation in the type system directly (unless we hardcode it for unref
). So instead of worrying about that I'm just asserting that results as any
to suppress a compiler warning.
Oh, and you had better spread that into f()
as f(...args.map(unref))
instead of f(args.map(unref))
if you expect it to work.
Anyway you can verify that this behaves as desired in the type system:
const myF = my(f);
// const myF: (a: MaybeRef<string>, b: MaybeRef<number>) => number
const myF1 = my(f1);
// const myF1: (a: MaybeRef<number>) => string
const myF2 = my(f2)
// const myF2: (a: MaybeRef<boolean>) => void
Upvotes: 2