#How to create the type for a function that has parameters and return based on the first parameter?

3 messages · Page 1 of 1 (latest)

latent agate
#

I would like the function to know based on the first parameter to know the next parameters and the return type that it will have.

The types I created:

export type EventCallbackMap = {
  cancelToken: (cancel: VoidFunction, node: FormKitNode) => void;
  before: (visit: PendingVisit, node: FormKitNode) => boolean| void;
  start: (visit: PendingVisit, node: FormKitNode) => void;
  progress: (progress: Progress, node: FormKitNode) => void;
  finish: (visit: ActiveVisit, node: FormKitNode) => void;
  cancel: (node: FormKitNode) => void;
  success: (page: Page, node: FormKitNode) => Promise<void> | void;
  error: (errors: Errors, node: FormKitNode) => Promise<void> | void;
};
export type EventNames = keyof EventCallbackMap;
export type EventCallback<TEventName extends EventNames> =
  (...params: Parameters<EventCallbackMap[TEventName]>) => ReturnType<EventCallbackMap[TEventName]>;
export type EventMap<TEventName extends EventNames> = Map<TEventName, Set<EventCallback<EventNames>>>;

export type EventOnFunction = <TEventName extends EventNames>(eventName: TEventName, callback: EventCallback<TEventName>) => void;
export type EventRunFunction = <TEventName extends EventNames>(eventName: TEventName, ...params: Parameters<EventCallbackMap[TEventName]>) => ReturnType<EventCallbackMap[TEventName]>;

export type AddonCallback = (on: EventOnFunction) => void;
export type AddonSet = Set<AddonCallback>;

And the function:

  const runEvents: EventRunFunction = (eventName, ...params) => {
    const _eventSet = eventList.get(eventName);
    if (!_eventSet) return;

    for (const event of _eventSet as Set<EventCallbackMap[typeof eventName]>) {
      const args = params as Parameters<EventCallbackMap[typeof eventName]>;

      const res = event(...args);

      if (!res) return res;
    }
  };
#

I got these 2 errors:

// From runEvents
Type '<TEventName extends keyof EventCallbackMap>(eventName: TEventName, ...params: Parameters<EventCallbackMap[TEventName]>) => false | void' is not assignable to type 'EventRunFunction'.
  Type 'false | void' is not assignable to type 'ReturnType<EventCallbackMap[TEventName]>'.
    Type 'false' is not assignable to type 'ReturnType<EventCallbackMap[TEventName]>'.ts(2322)

// From ...args
A spread argument must either have a tuple type or be passed to a rest parameter.ts(2556)
latent agate
#

This worked, but still not sure if its the best way to do it, if anyone has better understanding.

  const runEvents: EventRunFunction = (eventName, ...params) => {
    const set = (eventList.get(eventName) || new Set());

    if (eventName === 'before') {
      for (const event of set) {
        const res = event(...params);

        if (typeof res === 'boolean') return res as ReturnType<EventCallbackMap[typeof eventName]>;
      }
    } else if (['success', 'error'].includes(eventName)) {
      let promiseResolver = Promise.resolve();

      for (const event of set) {
        promiseResolver = promiseResolver.then(() => event(...params) as Promise<void>);
      }

      return promiseResolver as ReturnType<EventCallbackMap[typeof eventName]>;
    } else {
      for (const event of set) {
        event(...params);
      }
    }
  };