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.