Home > Back-end >  How to make a dependent element in a generic tuple?
How to make a dependent element in a generic tuple?

Time:10-08

Is it possible to declare a generic tuple in which one element depends on another?

For example, with this restriction:

interface Event<T, P extends any[] = []> {
  type: T;      // event name e.g. 'stop'
  payload: P;   // event parameters e.g. coordinates
}

type MovementEvents = Event<'stop'>| Event<'run', [number, number]>;

... can this implementation (incorrect):

type TransitionTuple<E extends Event<any, any>> = [
  EventType<E>,
  (...args: EventPayloadMap[E]) => void,
];

... somehow be transformed so that the second element (callback) would depend on the first one (type):

const transition: TransitionTuple<MovementEvents> = [
  'run',
  () => {}, // here must be an error, because the callback args don't match the event
];

?

CodePudding user response:

Before we begin, I renamed Event to EventThing because Event is already a thing and may cause confusion to some people (and TypeScript itself).

Distribute over the union and create a tuple for each member:

type TransitionTuple<U extends EventThing<any, any[]>> = U extends U ? [U["type"], (...args: U["payload"]) => void] : never;

All of the following now work:

const transitions: TransitionTuple<MovementEvents>[] = [
    [
        'run',
        () => {},
    ],
    [
        'run',
        (a, b) => {},
    ],
    [
        'stop',
        () => {},
    ]
];

Note that for the first entry, it is valid because functions do not need to use the parameters. Making this an error is a bad developer experience since developers will be forced to name the useless unused variables.

Playground

  • Related