pomber
pomber

Reputation: 23980

Conditional type for exctracting the type of a parameter of a function

I have this generic type that I use for extracting the type of the second parameter of a function:

type SecondParam<T> = T extends (a: any, b: infer R) => any
  ? R
  : never;

It works for most cases:

type T1 = SecondParam<(x: any, y: number) => void>; // number
type T2 = SecondParam<(x: any, y: { x: string[] }) => void>; // { x: string[] }

But when the second parameter is not present, I want it to give me void instead of {}:

type T3 = SecondParam<(x:any) => any> // is {}, I want void

I'm using SecondParam to define the type of another function:

type F<T> = (p: SecondParam<T>) => void;
type F1 = F<(x: any, y: number) => any>; // (p: number) => void
type F2 = F<(x: any, y: { x: string[] }) => any>; // (p: { x: string[] }) => void
type F3 = F<(x: any) => any>; // is (p: {}}) => void, but I want () => void

But it doesn't work well for the last case, when the second parameter is missing.

Upvotes: 0

Views: 76

Answers (1)

jmattheis
jmattheis

Reputation: 11115

The type confition (a: any, b: infer R) => any ? .. : .. is always true because omiting paramters doesn't create compile errors see this example which compiles:

const abc: (one: string, two: string, three: string) => void = (onlyFirst: string) => { };

If you really want () => void as type then you'd have to add a new type condition on the SecondParam type. Like this:

type SecondParam<T> = T extends (a: any, b: infer R) => any
  ? R
  : never;

type F<T> = T extends (a: any) => any
  ? () => void
  : (p: SecondParam<T>) => void;

type F3 = F<(x: any) => any>; // is () => void

If you want type never which you can't really assign, then you can change the SecondParam-type like this

type SecondParam<T> = T extends (a: any) => any
  ? never
  : T extends (a: any, b: infer R) => any
    ? R
    : never;

// you now, can't pass the second parameter as its never:
const f: F3 = () => { };
f(); // expects 1 argument

Upvotes: 2

Related Questions