1

I have a object with keys and functions as values. Now I want to create a function where you can only assign the keys where the corresponding key includes a callback function as last function parameter.

The callback may only have one argument and return type void.

interface Events {
  'valid0': (data: string, cb: () => void) => void, // valid
  'valid1': (data: number, cb: (data: string) => void) => void, // valid
  'invalid0': (data: string, cb: () => string) => void, // invalid return type of callback
  'invalid1': (data: string, cb: (string: string, number: number) => void) => void, // invalid number of callback arguments
}

type EventsWithCallback<E> = ???

type testFunction<Events> = (EventName: EventsWithCallback<Events>) => void

I am having problems to define this EventsWithCallback type. With the following I get the error: Type 'T[P]' does not satisfy the constraint '(...args: any[]) => void'. This is somehow logical. I tried typing T as Record<string, (...args: any) => void> but then I match all string.

type Last<T extends any[]> = T extends [...any, infer Last] ? Last : any;
type EventsWithCallback<T> = keyof { [P in keyof T as Last<Parameters<T[P]>> extends Function ? P : never]: T[P] };

Also extends Function matches any function and also any type.

Thanks for any kind of help. I hope the problem is understandable.

4
  • Why is the third one invalid? It's the exact same as the first one Commented Nov 9, 2021 at 11:33
  • Is it because the callback has a parameter? You haven't said it can't have one... Commented Nov 9, 2021 at 11:41
  • I don't understand which case should be valid and which not Commented Nov 9, 2021 at 11:55
  • The return type is string instead of void Commented Nov 9, 2021 at 12:07

1 Answer 1

1

Does this work? Playground Link

interface Events {
  'valid0': (data: string, cb: () => void) => void, // valid
  'valid1': (data: number, cb: (data: string) => void) => void, // valid
  'invalid0': (data: string, cb: () => string) => void, // invalid return type of callback
  'invalid1': (data: string, cb: (string: string, number: number) => void) => void, // invalid number of callback arguments
}

type Last<T extends any[]> = T extends [...any, infer Last] ? Last : any

type EventsWithCallback<T> = { 
  [P in keyof T]: 
    T[P] extends (...args: infer A) => void // Check if it is a function
      ? Last<A> extends (arg: any) => any // Check if the last parameter is a function with single parameter
        ? ReturnType<Last<A>> extends void // Check if it returns void
          ? P // return the parameter name
          : never
        : never
      : never
}[keyof T]

const testFunction = (eventName: EventsWithCallback<Events>) => {
  eventName = 'invalid0' // error
  eventName = 'invalid1' // error
  eventName = 'valid0'   // no error
  eventName = 'valid1'   // no error
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.