Typescript - adding an extra function parameter

1.2k Views Asked by At

I am trying to create a helper type which can convert a function of type

type Func1 = (id: string) => void;

to

type Func2 = (id: string, conf: { extraArg: boolean }) => void;

I found this answer which does work, however it names the first argument as head and the following one would be named id if I were to apply my example which is very misleading.

I cranked up this code a bit for my purposes

type Cons<T extends readonly any[], H> =
        ((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never;

type Push<T extends readonly any[], V>
        = T extends any ? Cons<T, void> extends infer U ?
            { [K in keyof U]: K extends keyof T ? T[K] : V } : never : never;

export type FunctionWithExtraArgument<F extends (...args: any[]) => any, Arg, R> = (...args: Push<Parameters<F>, Arg>) => R;

However I still couldn't get a complete solution which resolves the naming problem.

1

There are 1 best solutions below

4
On

No need to use code from above answer now. At the time of writing this answer there was not variadic tuple types.

Now you can achieve it with help of only one util:

type Func1 = (id: string) => void;


type Func2 = (id: string, conf: { extraArg: boolean }) => void;

type AddArgument<
  Fn extends (...args: any[]) => any,
  NextArg
  > =
  Fn extends (...arg: [...infer PrevArg]) => infer Return
  ? (...args: [...PrevArg, NextArg]) => Return
  : never;

// type Result = (args_0: string, args_1: {
//     extraArg: boolean;
// }) => void
type Result = AddArgument<Func1, { extraArg: boolean }>

Playground

If you want to you will always operate on one argument function, you can use this code:

type Func1 = (id: string) => void;


type Func2 = (id: string, conf: { extraArg: boolean }) => void;

type AddArgument<
  Fn extends (arg: any) => any,
  Conf
  > =
  Fn extends (id: infer Id) => infer Return
  ? (id: Id, conf: Conf) => Return
  : never;

// type Result = (id: string, conf: {
//     extraArg: boolean;
// }) => void
type Result = AddArgument<Func1, { extraArg: boolean }>

if I would have to "hardcode" every variable preceding the "conf" as I use 2-3 variables in a couple of cases

You can expect second generic argument as a tuple:

type Func1 = (id: string) => void;


type Func2 = (id: string, conf: { extraArg: boolean }) => void;

type AddArgument<
  Fn extends (...args: any[]) => any,
  NextArg extends any[]
  > =
  Fn extends (...arg: [...infer PrevArg]) => infer Return
  ? (...args: [...PrevArg, ...NextArg]) => Return
  : never;

// type Result = (args_0: string, args_1: {
//     extraArg: boolean;
// }) => void
type Result = AddArgument<Func1, [{ extraArg: boolean }, { anotherArg: 42 }]>