I'm wondering if it's possible to create dynamic type that would replace two params, such as in on: (eventName: EventName, callback: (data: any) => void) => void
with something closer to this on: (DynamicParams<EventName>) => void
, so it could extract callback's
type from a predefined set and use it instead of any
type.
I think it would be easier to explain with some code.
Say, I have set of different types of events:
const events = ['loaded', 'changed'];
type ArrayElement<ArrayType extends readonly unknown[]> = ArrayType[number];
type EventName = ArrayElement<typeof events>
and a function that runs only when a specific notification has been emitted;
const on = (eventName: EventName, callback: (data: any) => void) => {}
But I want to be able to use this function with callback that accepts different parameter types, without checking for the type manually and without any casting, for example:
on('loaded', (list: Entry[]) => {
// do something with loaded array of elements;
}
on('changed', (index: number) => {
// do something with index of a changed entry;
}
Is there a way to create a mapped type that would accept EventName and return a specific type to a given event?
Something like that:
const on(eventName: EventName, DynamicMethod<'eventName'> => void) => {}
const on(DynamicParams<'eventName'>) => {}
Assume, I would need to replace event object and create a type in its place, instead:
type events = [
{
name: 'loaded',
method: (list: Entry[]) => void
},
{
name: 'changed',
method: (index: number) => void
}
]
But I'm not sure how to extract name values (not types of the values).
CodePudding user response:
If you define a map of event names to their data type:
type EventMap = {
loaded: Entry[];
changed: number;
};
You can make on
generic:
const on = <E extends EventName>(eventName: E, callback: (data: EventMap[E]) => void) => {}
on('loaded', (list) => {
// ^? Entry[]
});
on('changed', (index) => {
// ^? number
});
CodePudding user response:
There are two ways to make this work:
Using call signatures:
interface OnEvent {
(eventName: 'loaded', callback: (list: Entry[]) => void): void;
(eventName: 'changed', callback: (index: number) => void): void;
}
const on: OnEvent = (eventName, callback) => {
// Choose how to register the event here...
}
Or function overloads:
function on(eventName: 'loaded', callback: (list: Entry[]) => void): void;
function on(eventName: 'changed', callback: (index: number) => void): void;
function on(eventName: string, callback: (data: any) => void) {
// Choose how to register the event here...
}