1

I want to create a function addEventListener

This function has two arguments, the event and the listener.

I have some specific events: ex. onScroll, onClick, onDoubleClick.

Depending on the event I want to have a separate signature / type for the listener argument.

So if I start typing:

addEventListener('onClick', (param: OnClickType) => // here I want to resolve a certain listener type )

// or

addEventListener('onScroll', (param: OnScrollType) => // here I want to resolve a certain listener type )

Is this possible in typescript ?

[EDIT]

Something like this:

enter image description here

[EDIT2]

export type OnClick = (event: 'onclick', listener: (x: number) => void) => void
export type OnScroll = (event: 'onscroll', listener: (x: string) => void) => void

export type EventListener = OnClick | OnScroll;

const addEventListener: EventListener = (ev, ls) => {
    console.log(ev, ls);
    return;
}

addEventListener('onclick', )

What I want is to infer the type of the second argument of the function (listener) depending on the value of the first argument (event).

9
  • Yes, just use overloads Commented Dec 16, 2021 at 14:18
  • @AlekseyL. It does not seem clear to me, because it does not have to do with the parameters itself but with their values instead, I mean the value of the event decides the type of the listener which I have no idea how to do Commented Dec 16, 2021 at 14:20
  • look at interfaces typescriptlang.org/docs/handbook/interfaces.html Commented Dec 16, 2021 at 14:21
  • 3
    Please provide a minimal reproducible example that clearly demonstrates the issue you are facing. Ideally someone could drop the code into a standalone IDE like The TypeScript Playground (link here!) and immediately get to work solving the problem without first needing to re-create it. So there should be no pseudocode, typos, unrelated errors, or undeclared types or values. Commented Dec 16, 2021 at 14:27
  • Please replace/supplement images of code/errors with plaintext versions. Commented Dec 16, 2021 at 14:47

1 Answer 1

1

If you have just a few hardcoded function types like OnClick and OnScroll:

type OnClick = (event: 'onclick', listener: (x: number) => void) => void
type OnScroll = (event: 'onscroll', listener: (x: string) => void) => void

and you want to say that an EventListener can behave like all of them, you can manually rewrite both call signatures into a single, overloaded function type:

type EventListener = {
    (event: 'onclick', listener: (x: number) => void): void;
    (event: 'onscroll', listener: (x: string) => void): void;
}

const addEventListener: EventListener = (ev, ls) => {
    console.log(ev, ls);
    return;
}

addEventListener('onclick', x => console.log(x.toFixed(2))); // okay
addEventListener('onscroll', x => console.log(x.toUpperCase())); // okay

Equivalently, you can just write EventListener as the intersection of the existing function types:

type EventListener = OnClick & OnScroll; 

This puts both call signatures into the same type, as an overloaded function type with the OnClick signature first and the OnScroll signature second. So when you call an EventListener the compiler will try to resolve the OnClick call signature first, and then fall back to OnScroll if that fails. Since "onclick" and "onscroll" are mutually exclusives, the order doesn't really matter here. But there are cases where order of call signature resolution does matter, and then a type F1 & F2 will behave differently from F2 & F1 (which might be surprising since intersections are conceptually unordered).


If you have a bunch of different event names and payload types and you don't want to manually write out one call signature for each such pair, you can forgo multi-call signature overloads in favor of a single generic function:

interface EventMap {
    onclick: number;
    onscroll: string;
}

type EventListener = <E extends keyof EventMap>(
    event: E, listener: (x: EventMap[E]) => void
) => void;

This behaves similarly (the prior implementation of and calls to addEventListener() still work), but now you can just add a new line to EventMap for each new event name / payload type pair.

Playground link to code

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.