Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverse inference event types from $emit #60

Closed
johnsoncodehk opened this issue Feb 11, 2021 · 1 comment
Closed

Reverse inference event types from $emit #60

johnsoncodehk opened this issue Feb 11, 2021 · 1 comment
Labels
help wanted Extra attention is needed

Comments

@johnsoncodehk
Copy link
Member

I spent a lot of time on this issue, and current TS version seems can't solve it.

Problem

Given a $emit type with unknow overloads length:

type Emit = {
  (event: 'event-1', str: string): void;
  (event: 'event-2', num: number): void;
  (event: 'event-3', bool: boolean): void;
  // more events...
  (event: 'event-n', str: string, num: number, bool: boolean): void;
}

How to get the event function type with specify event name?

type EmitEvent<F, E> = ...;

type Event_1 = EmitEvent<Emit, 'event-1'>; // (str: string) => void
type Event_2 = EmitEvent<Emit, 'event-2'>; // (num: number) => void
type Event_3 = EmitEvent<Emit, 'event-3'>; // (bool: boolean) => void
// more events...
type Event_n = EmitEvent<Emit, 'event-n'>; // (str: string, num: number, bool: boolean) => void

What Now

For limited overloads length (for example 1 ~ 4), it can resolve with:

type EmitEvent<F, E> =
   F extends {
      (event: E, ...payload: infer P): infer R // match 'event n - 3'
      (...args: any): any
      (...args: any): any
      (...args: any): any
   } ? (...payload: P) => R
   : F extends {
      (event: E, ...payload: infer P): infer R // match 'event n - 2'
      (...args: any): any
      (...args: any): any
   } ? (...payload: P) => R
   : F extends {
      (event: E, ...payload: infer P): infer R // match 'event n - 1'
      (...args: any): any
   } ? (...payload: P) => R
   : F extends {
      (event: E, ...payload: infer P): infer R // match 'event n'
   } ? (...payload: P) => R
   : unknow /* or `(...payload: any[]) => any` whatever */; // match 'event n < n - 3'

(Note: Increasing the number of overloads in this way inferred time will increase exponentially, so it is not feasible to detect the number of overloads dynamically generating EmitEvent<F, E>.)

Some libraries have more than 10 events overloads, so this is still a problem to be solved.

References:

@johnsoncodehk johnsoncodehk added the help wanted Extra attention is needed label Feb 11, 2021
@johnsoncodehk
Copy link
Member Author

resolve with DefineComponent, thanks to @pikax !

type ExtractEmits<T> = T extends DefineComponent<
	any,
	any,
	any,
	any,
	any,
	any,
	any,
	infer E
>
	? E
	: never;
type ReturnVoid<T> = T extends (...payload: infer P) => any ? (...payload: P) => void : T;
type EmitEvent<T, E extends keyof ExtractEmits<T>> = ReturnVoid<ExtractEmits<T>[E]>;

const dd = defineComponent({
  emits: {
    "event-1"(str: string) {},
    "event-2"(num: number) {},
    "event-3"(bool: boolean) {},
    "event-n"(str: string, num: number, bool: boolean) {},
  },
});
type Event_1 = EmitEvent<typeof dd, 'event-1'>; // (str: string) => void
type Event_2 = EmitEvent<typeof dd, 'event-2'>; // (num: number) => void
type Event_3 = EmitEvent<typeof dd, 'event-3'>; // (bool: boolean) => void
type Event_n = EmitEvent<typeof dd, "event-n">; // (str: string, num: number, bool: boolean) => void

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

1 participant