简体   繁体   中英

How to overload and bind argument types in Typescript?

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:

在此处输入图像描述

[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 ).

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM