1

I am trying to have two distinct overloads for event emission: One that maps to custom events, with well-known listener arguments (And thus dispatch time arguments)

export type EngineEvents
  = ['window-exit', () => void]
  | ['test', (code: number) => void]
  | ['pre-init', () => void]
  | ['post-init', () => void]
  | ['tick-start', () => void]
  | ['draw-start', () => void]
  ;

The problem here is to map those types to the dispatch type, without having to repeat all over again, I've tried the following:

export type EventArgs = [EngineEvents[0], ...Parameters<EngineEvents[1]>];

But that expands to a tuple that contains a union in each cell, which is not what I want. I would like to have the tupple mapped the other way round, for example:

// Instead of
type A = [ 'a' | 'b', 1 | 2 ];
// Have this:
type B = [ 'a', 1 ] | [ 'b', 2 ];

I've tried using the T extends any? whatever : never idiom as suggested by this answer: TypeScript: Map union type to another union type

But it didn't work out.

export type ArgumentExpand<U> = U extends any[]? [U[0], ...U[1]] : never;

Is there any way of mapping the union, one-by-one so when accessing the first and second element of the tuple it does not mix them up?

Like a map operation, but for the type.

Sigma Octantis
  • 903
  • 1
  • 8
  • 25

1 Answers1

2

Distributive conditional type will do the job here:

type MapArgs<E> = E extends [string, (...args: any[]) => any]
    ? [E[0], ...Parameters<E[1]>] : never;

type EventArgs = MapArgs<EngineEvents>;

Playground

Aleksey L.
  • 35,047
  • 10
  • 74
  • 84