Make similar function but exclude the last argument


I’m trying to create a type B which defines same function as type A but omits the last parameter. I’ve tried the following but it still asks for 2 parameters.

type Callback = (msg: string) => void;

type A = (A: string, B: Callback) => void;
type B = <U extends Exclude<Parameters<A>, Callback>>(...args: U) => void;

const b: B = (...args) => {};

b("some string"); // Expected 2 arguments, but got 1.

How can I do this properly?

>Solution :

You can use conditional type inference to exclude the last parameter from a function type:

type ExcludeLastParam<T extends (...args: any) => any> =
    T extends (...args: [...infer I, any]) => infer R ? (...args: I) => R : never;

This matches T against (...args: [...I, any]) => R for some I and R that the compiler infers using the infer keyword. Note that [...I, any] is a variadic tuple type and by matching [...I, any] against a tuple type (like, say, [a: string, b: Callback]), the compiler will infer I to be the "initial" part of the tuple excluding the last entry (since the any type will match anything) (like, say, [a: string]).

Let’s test it out:

type Callback = (msg: string) => void;   
type A = (a: string, b: Callback) => void;

type B = ExcludeLastParam<A>;
// type B = (a: string) => void


type C = (a: string, b: number, c: boolean, d: Date) => string;
type D = ExcludeLastParam<C>
// type D = (a: string, b: number, c: boolean) => string

Looks good!

Note that your version doesn’t work because, while the Parameters<T> utility type can be used to extract the parameter list as a tuple type, the Exclude<T, U> utility type does not filter tuple types; instead it filters union types. Since Parameters<A> is of type [a: string, b: Callback], filtering out Callback isn’t going to do anything (because Parameters<A> is not a union… or maybe it’s a single member union, and that member is not assignable to Callback).

Playground link to code

Leave a ReplyCancel reply