Home > Net >  Dynamic type for function parameters with extraction of predefined types
Dynamic type for function parameters with extraction of predefined types

Time:11-03

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
});

Playground

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...
}
  • Related