1

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.

2
  • Please create a fiddle with definitions and usage on which you expect to work. It will be much easier to help you then. Commented Jun 21, 2021 at 15:03
  • I made guesses about what unref and MaybeRef might look like, and also what "it does not work" might mean (that is, "It wraps all arguments"). If I made mistakes in my guesses, please consider modifying the code here to constitute a minimal reproducible example suitable for demonstrating the issue. Commented Jun 21, 2021 at 15:18

1 Answer 1

2

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

Playground link to code

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.