12
type SomeFunc = (a:string, b:number, c:someCustomType) => number;

I want to create a type that is just like the one above, except there is a single parameter added at the end. Let's say, d:number;

type SomeFuncAltered = (a:string, b:number, c:someCustomType, d:number) => number;

I do not want to craft the entire type manually though, I'm pretty sure there's a smart trick with Parameters<func> to be used here.

John Smith
  • 3,863
  • 3
  • 24
  • 42
  • 1
    If it was being added at the front, you could do this: `type Alter any> = (extraArg: number, ...args: Parameters) => ReturnType;`. I don't know how to add it at the end though, since rest args can only be at the end. – Nicholas Tower Feb 20 '20 at 15:56
  • Front is simple :) but end is not – Maciej Sikora Feb 20 '20 at 15:58

3 Answers3

13

As the accepted answer here doesn't seem to work for me (using TS 4), I wrote my own type that adds any number of parameters to the end of a function.

type AddParameters<
  TFunction extends (...args: any) => any,
  TParameters extends [...args: any]
> = (
  ...args: [...Parameters<TFunction>, ...TParameters]
) => ReturnType<TFunction>;

Usage:

type SomeFunc = (a: string, b: number, c: someCustomType) => number;
type SomeFuncAltered = AddParameters<SomeFunc, [d: number]>;
// SomeFuncAltered = (a:string, b:number, c:someCustomType, d:number) => number;

Note: This solution also allows you to add named parameters.

Melody Universe
  • 171
  • 1
  • 4
  • 1
    THIS IS AMAZING, thank you! If anyone doesn't want to make a generic type for whatever reason you can just replace the arguments. For example, I'm extending the fetch API for a Keycloak OAuth fetch wrapper: `type FetchSecure = ( ...args: [...[keycloak: KeycloakInstance], ...Parameters] ) => ReturnType;` Note Keycloak argument had to come first since the init? param of fetch is optional :) – Gifford N. Jul 14 '22 at 21:36
3

Its possible but quite sophisticated. More info can be found in this answer from @jcalz - Push type to the end of the tuple with skipping optional .

In your case we can reuse some utilities from above answer, exactly it would be Cons and Push and by using them make final type you need AddArgument. Consider:

type SomeFunc = (a: string, b: number, c: string) => number;

// some utility types for working with tuples
type Cons<H, T extends readonly any[]> =
    ((head: H, ...tail: T) => void) extends ((...cons: infer R) => void) ? R : never;

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

// final type you need
type AddArgument<F, Arg> = 
F extends ((...args: infer PrevArgs) => infer R) 
? (...args: Push<PrevArgs, Arg>) => R : never

// function type with added boolean argument at the end
type NewFunction = AddArgument<SomeFunc, boolean>
Maciej Sikora
  • 19,374
  • 4
  • 49
  • 50
2

You can use additional type for the base function args:

type FuncBaseArgs = {
  a: string;
  b: number;
  c: boolean;
}

type SomeFunc = ({...obj }: FuncBaseArgs) => number;

type SomeFuncAltered = ({...obj }: FuncBaseArgs, d: number) => number;
Oron Bendavid
  • 1,485
  • 3
  • 18
  • 34