Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript infer function parameter unions

I currently have an interface with overloaded functions like so:

export interface IEvents {
  method(): boolean;
  on(name: 'eventName1', listener: (obj: SomeType) => void): void;
  on(name: 'eventName2', listener: (obj: SomeType) => void): void;
  on(name: 'eventName3', listener: (obj: SomeType) => void): void;
  on(name: 'eventName4', listener: (obj: SomeType) => void): void;
  on(name: 'eventName5', listener: (obj: SomeType) => void): void;
  on(name: 'eventName6', listener: () => void): void;
  on(name: 'eventName7', listener: (obj: SomeType) => void): void;
  on(name: 'eventName8', listener: (obj: SomeType) => void): void;
}

I am trying to get the union type of event names like so:

eventName1 | eventName2 | ...

I have tried the following, but when I infer the type it seems to only pick one of the name values and not a union of all of them.

export type TEventExtension<T extends IEvents> {
  [K in keyof T]: K extends 'on' ? TEventListenerName<T[K]> : never;
}[keyof T];
export type TEventListenerName<T> = T extends (name: infer N, listener: (obj?: infer E) => void) => void ? N : never;
const ext: TEventExtension<IEvents> = void 0 as any; // Type: 'eventName8'

I have also tried using an accumulator type to keep track of the unions, but Typescript doesnt allow recursive generics.

Any ideas on how I can accomplish this?

Edit: The interface with the overloaded definitions exists in an external module. I am trying to avoid c+ping from the external definitions to my definitions, and instead have it build the type automatically.

like image 441
Pat Avatar asked May 22 '18 16:05

Pat


People also ask

How do you pass a function as a parameter in TypeScript?

Similar to JavaScript, to pass a function as a parameter in TypeScript, define a function expecting a parameter that will receive the callback function, then trigger the callback function inside the parent function.

How does TypeScript infer types?

TypeScript infers types of variables when there is no explicit information available in the form of type annotations. Types are inferred by TypeScript compiler when: Variables are initialized. Default values are set for parameters.

What does ?: Mean in TypeScript?

What does ?: mean in TypeScript? Using a question mark followed by a colon ( ?: ) means a property is optional. That said, a property can either have a value based on the type defined or its value can be undefined .


1 Answers

This doesn't need to be a separate answer, but it's hard to get this in a comment: overloads specifically do not work the way you want with type inference in conditional types:

When inferring from a type with multiple call signatures (such as the type of an overloaded function), inferences are made from the last signature (which, presumably, is the most permissive catch-all case). It is not possible to perform overload resolution based on a list of argument types.

declare function foo(x: string): number;
declare function foo(x: number): string;
declare function foo(x: string | number): string | number;
type T30 = ReturnType<typeof foo>;  // string | number

If you can't turn the overloads into a single function with a union of parameters (using conditional types to get listener for "eventName6" correct), then I don't know of a way to do this programmatically.

like image 81
jcalz Avatar answered Oct 18 '22 08:10

jcalz