Identifying the Challenge
If we examine the following code:
// Possible events that may be received:
enum EventType { PlaySong, SeekTo, StopSong };
// Corresponding callbacks:
type PlaySongCallback = (name: string) => void;
type SeekToCallback = (seconds: number) => void;
type StopSongCallback = () => void;
Within the API provided to me, I have the ability to register a callback using
declare function registerCallback(t: EventType, f: (...args: any[]) => void);
My goal is to eliminate the need for any[]
and ensure that only correctly-typed callback functions can be registered.
Possible Resolution?
It occurred to me that this approach might work:
type CallbackFor<T extends EventType> =
T extends EventType.PlaySong
? PlaySongCallback
: T extends EventType.SeekTo
? SeekToCallback
: T extends EventType.StopSong
? StopSongCallback
: never;
declare function registerCallback<T extends EventType>(t: T, f: CallbackFor<T>);
// Successfully registering:
registerCallback(EventType.PlaySong, (name: string) => { /* ... */ })
// These attempts are invalid:
// registerCallback(EventType.PlaySong, (x: boolean) => { /* ... */ })
// registerCallback(EventType.SeekTo, (name: string) => { /* ... */ })
This method appears to be quite efficient and effective! It almost feels like working with dependent types: essentially creating a function that maps values to types.
Despite this achievement, I am not fully aware of TypeScript's type system capabilities. Perhaps there exists an even more optimal way to map enum values to types without relying on such an extensive conditional type structure as demonstrated above. (In practice, managing numerous events becomes cumbersome: my IDE displays a complex expression when hovering over CallbackFor
, and maintaining proper indentation after each :
within the type definition proves challenging.)
The Inquiry
Is there a superior technique for mapping enum values to types in this manner? Is it feasible to avoid lengthy conditional types like the one presented here? (Given a high volume of events, the current setup becomes unwieldy: my code editor showcases a verbose expression upon inspecting CallbackFor
, and adherence to strict formatting guidelines poses issues.)
I am intrigued by the prospect of devising an object that maps enum values directly to types, enabling the declaration of registerCallback
employing T
and CallbackFor[T]
. Nonetheless, such a strategy seemingly remains elusive. Any insights or suggestions welcomed!