19
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.

2
  • 2
    If it was being added at the front, you could do this: type Alter<T extends (...args: any) => any> = (extraArg: number, ...args: Parameters<T>) => ReturnType<T>;. I don't know how to add it at the end though, since rest args can only be at the end. Commented Feb 20, 2020 at 15:56
  • Front is simple :) but end is not Commented Feb 20, 2020 at 15:58

3 Answers 3

22

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.

Sign up to request clarification or add additional context in comments.

1 Comment

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<typeof fetch>] ) => ReturnType<typeof fetch>; Note Keycloak argument had to come first since the init? param of fetch is optional :)
4

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>

1 Comment

That is... epic.
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;

1 Comment

Nice. Not Generic, but very practical.

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.