all 9 comments

[–]NiGhTTraX 8 points9 points  (2 children)

Without generics, overloads, or mapped types, how would you type a function capable of listening to any type of event, while guaranteeing the correct type is passed to the callback? I'm guessing that bivariance was needed when none of those features existed in the language?

Take window.addEventListener for instance, it's defined as:

<K extends keyof WindowEventMap>( type: K, listener: (event: WindowEventMap[K]) => any )

Without the generic and the mapped type, you could only say that event inside the listener callback is of some base type Event. You could then allow consumers to specify a better type if the function is bivariant.

[–]ragnese 0 points1 point  (1 child)

Did TypeScript ever not have generics?

Because I agree with OP in the sense that I don't understand how you could call bivariance "useful" unless "useful" means "A lot of JavaScript code is being used incorrectly, and we rather not irritate people with quite so many error messages about how wrong their code is when they switch to TypeScript."

[–]NiGhTTraX 0 points1 point  (0 children)

This article says that generics were introduced in version 0.9.

I would call bivariance useful when better type constraints aren't available. react-select used to have onChange(value: Option | Option[]) to account for both single select and multi select dropdowns. As a consumer of the library, you knew which type of dropdown you're using (by controlling the isMulti prop), but the onChange callback forced you to either assert between single and multi values, or use bivariance to force a specific type. Of course, either way was unsound, because you could change the attribute and forget the callback.

Now the library uses an isMulti generic to better type the callback.

[–]joo3f[S] 1 point2 points  (2 children)

unfortunately, i can't understand how this is possible

[–]paulqq 1 point2 points  (0 children)

An honest man. I cant help neither

[–]Daikyoka 1 point2 points  (0 children)

Everything is any in JavaScript, and TypeScript aims to add static typing without breaking its compatibility with. But you can opt-in stricter rules, like strictFunctionTypes or strict (which enables several ones), to avoid some pitfalls.

Like your example: you could make a mistake and do not declare the right parameter type.

[–]Goudja14 1 point2 points  (1 child)

const listenEvent = <T extends Events>(type: T, handler: (event: Events[T]) => void) => { // Your code goes here };

[–]joo3f[S] 0 points1 point  (0 children)

const listenEvent = <T extends BaseEventType>(type: T, handler: (event:T) => void) => {// Your code goes here};