Subscribe on changes!

Breaks with @types/node@18.8.5, Type '...' is not assignable to type 'Event'. ts2322

avatar
Oct 17th 2022

Vue version

(Not related)

Link to minimal reproduction

https://www.typescriptlang.org/play?jsx=1#code/JYWwDg9gTgLgBAbwL5wGZQiOByAbgVwFNsAoAejLgAEYBnAWkIA8xCBjGRqDKOAG0LwwfAIYBPAOYZ8AOwAmcYDNowRfPtRhjWtMjIhzCJUJFhwtrRCnSYc+w6RJsIy+ABU4AXjgAeOcFw4FwBhPmA2AGtPBAAKAEovAD4rFDJEkiA

Steps to reproduce

Make sure to have vue and @types/node installed.

const T = <div onClick={() => {}} />

What is expected?

No type error

What is actually happening?

Type '() => void' is not assignable to type 'MouseEvent'. (2322)

System Info

(Unrelated)

Any additional comments?

This is caused by https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59905.

Vue uses the following code to get generate the signature of event handlers:

https://github.com/vuejs/core/blob/9617dd4b2abc07a5dc40de6e5b759e851b4d0da1/packages/runtime-dom/types/jsx.d.ts#L1303-L1305

And Vue maintains an event map. It tests each property is assignable to Function (interface).

On the other side, @types/node introduces a new change to include browser types that are implemented in Node, which is, unfortunately, incompatible with DOM.

The PR is intended to not re-declaring the existing type (from libdom) if possible (which is not possible in TypeScript), but they're doing it in the wrong way.

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/c52afba497870de8fe73942dc605081576f149e0/types/node/dom-events.d.ts#L107

It augmented the global Event interface with __Event

declare global {
    interface Event extends __Event {}
    // ...
}

And __Event is defined in the following way:

https://github.com/DefinitelyTyped/DefinitelyTyped/blob/c52afba497870de8fe73942dc605081576f149e0/types/node/dom-events.d.ts#L11

type __Event = typeof globalThis extends { onmessage: any, Event: infer T }
//                                                                ~~~~~~~ wrong
? T
: // Node's definition ...

It tries to reuse the global Event value (which is NOT the global interface Event), which means __Event is the constructor of DOM Events.

Because (AFAIK) it is impossible to "reflect" a type out to solve this problem.

// Note: globalThis is a namespace
namespace Test {
  export interface Name { value: 1 }
  export var Name: Function & { value: 2 }
}

type _ = [
  Test.Name['value'], // 1
  typeof Test.Name['value'], // 2

  typeof Test extends { Name: infer N } ? N : never
  // Function & { value: 2 }
]

This brings us back to the Vue side.

With the augmented global Event interface, now all DOM events (MouseEvent, KeyboardEvent, etc) are now inherited from the global Event constructor (from the value namespace).

type EventHandlers<E> = {
  [K in keyof E]?: E[K] extends Function ? E[K] : (payload: E[K]) => void
}

And E[K] (Events) now all passes the extends Function test and makes all DOM JSX types broken (they're now typed as onClick?: MouseEvent | undefined).

avatar
Oct 17th 2022

Related PR #6855

avatar
Oct 21st 2022

Comment by sodatea in the PR: 15 hours ago

FYI the issue has been fixed in @types/node v18.11.1+ so you don't have to wait for this PR to be merged to update your @types/node version.