Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript - Create Union From Object

I'm creating an EventEmitter in typescript, and I can't figure out a way to do the following:

Say I have an interface like this:

interface EventEmitterSubscription { dispose(): void }

// here it is
interface EventEmitter<T extends { [key: string]: any }> {
  onAnyEvent(callback: (event: { type: ???, payload: ??? }) => void): EventEmitterSubscription
  // ...
}

I can't find a way to type the onAnyEvent callback such that, for example, for an eventEmitter like this:

EventEmitter<{ onNumberReceived: number, onCatReceived: { cat: ICat }, onPersonNameReceived: string }>

The onAnyEvent field would have the type

onAnyEvent(callback: (event: { type: 'onNumberReceived', payload: number } | { type: 'onCatReceived', payload: { cat: ICat } } | { type: 'onPersonNameReceived', payload: string }) => void): EventEmitterSubscription

Currently my implementation has the interface look like:

onAnyEvent(callback: (event: { type: keyof T, payload: T[keyof T] }) => void): EventEmitterSubscription

except that currently doesn't work, as for example that would produce the types onAnyEvent(callback: (event: { type: 'onNumberReceived', payload: number } | { type: 'onNumberReceived', payload: { cat: ICat } } | /* ... */) => void): EventEmitterSubscription

So how can I type the onAnyEvent field?

like image 274
notrota Avatar asked Mar 07 '23 02:03

notrota


1 Answers

One possible way to create a union is to have a mapped type with each property having the type of one element of desired union, like this:

type EventTypeMap<T extends { [key: string]: {} }> =
    { [K in keyof T]: { type: K; payload: T[K] } };

Then you can define generic union type

type CallbackEventTypes<T extends { [key: string]: {} }> =
     EventTypeMap<T>[keyof EventTypeMap<T>] 

and use it

interface EventEmitterSubscription { dispose(): void }

interface EventEmitter<T extends { [key: string]: {} }> {
  onAnyEvent(callback: (event: CallbackEventTypes<T>) => void): EventEmitterSubscription
  // ...
}

interface ICat { meow() }

type Emitter = EventEmitter<{ onNumberReceived: number, onCatReceived: { cat: ICat }, onPersonNameReceived: string }>

type HandlerType = Emitter['onAnyEvent'];

// inferred as
//type HandlerType = 
// (callback: (event: { type: "onNumberReceived"; payload: number; }
//                  | { type: "onCatReceived"; payload: { cat: ICat; }; }
//                  | { type: "onPersonNameReceived"; payload: string; }
//            ) => void) => EventEmitterSubscription
like image 157
artem Avatar answered Mar 11 '23 20:03

artem