Skip to content

Conversation

@cosmic-pixel-painter
Copy link

@cosmic-pixel-painter cosmic-pixel-painter commented Dec 6, 2025

This PR fixes microsoft/TypeScript#62075.

Originally, I created this #2148 (comment), but closed it as the that approach wasn't ideal.

I started with Omit<GlobalEventHandlers, 'onerror'> as an approach. However, I quickly realized it isn't the correct path and creates a whole slew of new problems due to how event handlers are mapped to different elements during the emit phase. (if you're curious to see what exactly I ran into, I can push up a separate PR).

What this change does:

Introduces a new type DocumentOrGlobalOnErrorEventHandler which extends the OnErrorEventHandlerNonNull type with HTMLElement specific handlers. This type then overrides GlobalEventHandlers onerror property. This allow us to be backwards compatible and fix the bug.

This did require a change to both types.ts and emitter.ts so that exposed was accounted for when creating a new typedef. I needed the new DocumentOrGlobalOnErrorEventHandler to only be exposed on the dom library.

The onerror of GlobalEventHandlers cannot be overridden by the overrideType.ts file. I tried to a few times, in both mixin-ins and interfaces. It turns out it has to be overidden in the patches/events.kdl file.

Let me know if I missed anything or another approach would be better. I think this threads the needle nicely as it works only on dom elements and still provides both the global and HTMLElement specific onerror handling types.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 6, 2025

Thanks for the PR!

This section of the codebase is owned by @saschanaz - if they write a comment saying "LGTM" then it will be merged.

@cosmic-pixel-painter
Copy link
Author

This one is ready for review @saschanaz.

@saschanaz saschanaz self-requested a review December 9, 2025 10:02
Copy link
Collaborator

@saschanaz saschanaz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the long delay, wasn't quite sure what to do with this PR. Now I sorta do...

*
* [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
*/
addEventListener(type: string, listener: ((event: Event) => void) | ((event: UIEvent) => void)): void;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not quite right as this affects all other events. And still not sure about UIEvent:

  1. MDN's description is a copypaste of what UI Events spec says in https://w3c.github.io/uievents/#event-type-error:

    UIEvent if generated from a user interface, Event otherwise.

  2. But then the same spec says otherwise in https://w3c.github.io/uievents/#event-types-list, where it only says Error for error event. So does HTML in https://html.spec.whatwg.org/multipage/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects.
  3. Nothing in UI Events says exactly when UIEvent is used for error event.
  4. UI Events also argues load event uses UIEvent, which is untrue.
  5. UI Events hasn't been well maintained, see also the "Future of UI Events" discussion in https://docs.google.com/document/d/1fOjXiGUFf_krkfYaWf5drbqzY7CTxn7EcDYr5ZWjzFg/preview.

So I'd like to suggest excluding UIEvent in this PR.

onended: ((this: GlobalEventHandlers, ev: Event) => any) | null;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/error_event) */
onerror: OnErrorEventHandler;
onerror: DocumentOrGlobalOnErrorEventHandler;
Copy link
Collaborator

@saschanaz saschanaz Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also affects all other interfaces so it partially repeats the same issue of OnErrorEventHandler, and also not quite I suggested in #2148 (comment)...

I was thinking about:

interface DocumentOrElementEventHandlers extends GlobalEventHandlers {
  onerror: ((this: GlobalEventHandlers, ev: Event) => any) | null;
};

And make others inherit this instead of GlobalEventHandlers when appropriate. Wdyt?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that as-is has an assignability issue. Hmm.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do have extendConflictsBaseTypes in emitter.ts which can help here... it's sad to add more there but it does make sense here.

Copy link
Collaborator

@saschanaz saschanaz Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing so would make subinterfaces to fail to assign to a variable typed as GlobalEventHandlers, which is a bit worrisome.

Hmmmmm. Maybe a better way is to make it reversed?

interface GlobalEventHandlers {
  // remove OnErrorEventHandler and make it general here  
  onerror: ((this: GlobalEventHandlers, ev: Event) => any) | null;
}

interface GlobalErrorEventHandlers extends GlobalEventHandlers {
  onerror: OnErrorEventHandler;
}

// Make global interfaces extend GlobalErrorEventHandlers instead of GlobalEventHandlers.
// Ideally this should be replaced automatically by emitter than overridingTypes, as
// the latter wouldn't support any upcoming new globals.
// Checking `global` property for each interface should allow it. 
interface Window extends GlobalErrorEventHandlers {
  // ...
}

Copy link
Collaborator

@saschanaz saschanaz Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Err. Actually if we are going to check globals then we don't need GlobalErrorEventHandlers at all. The emitter can just add an overridden onerror property directly to each global interface.

interface GlobalEventHandlers {
  // Remove OnErrorEventHandler and make it general here.
  // Replacing it to a general EventHandler would do this automatically.
  onerror: ((this: GlobalEventHandlers, ev: Event) => any) | null;
}

interface Window extends GlobalEventHandlers {
  onerror: OnErrorEventHandler;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTMLElement inherits onerror and addListener("error" definitions that are unique to Window

2 participants