Skip to content

Fix Element.matches strict narrowing#2487

Open
cjc0013 wants to merge 2 commits into
microsoft:mainfrom
cjc0013:fix-matches-strict-narrowing
Open

Fix Element.matches strict narrowing#2487
cjc0013 wants to merge 2 commits into
microsoft:mainfrom
cjc0013:fix-matches-strict-narrowing

Conversation

@cjc0013

@cjc0013 cjc0013 commented May 28, 2026

Copy link
Copy Markdown

Fixes microsoft/TypeScript#63497.

This follows the direction discussed in that issue: matches() should only act as a type guard when the selected tag's element type is a strict subtype of the current receiver type. For tags that map back to the same receiver type, it should still be accepted but return boolean, so the false branch does not narrow the receiver to never.

Summary

  • restrict Element.matches() type-predicate overloads to strict subtype matches
  • keep tag-name boolean overloads for same-type matches
  • regenerate DOM baselines
  • add a compile-only regression fixture for the reported false-branch never behavior

Validation

  • npm run generate
  • npm test
  • focused local TypeScript probe: the current baseline produced Property 'id' does not exist on type 'never'; the patched baseline compiles cleanly

@github-actions

Copy link
Copy Markdown
Contributor

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.

@cjc0013

cjc0013 commented May 28, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

Comment thread src/build/emitter.ts Outdated

function emitElementMatchesMap() {
printer.printLine(
"type ElementMatchesMap<T, U> = { [K in keyof T as T[K] extends U ? U extends T[K] ? never : K : never]: T[K] };",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is hard to parse, can you use something like ElementMap and ElementType than T and U? And also with parentheses.

It also kinda works weird, keyof ElementMatchesMap<HTMLElementTagNameMap, HTMLDivElement> is somehow "object" | "col" | "colgroup" | "embed" | "hr" | "iframe" | "img" | "input" | "legend" | "table" | "tbody" | "td" | "tfoot" | "th" | "thead" | "tr" ? It should be "div", right?

@cjc0013 cjc0013 Jun 30, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks, that makes sense. I updated the helper to make the strict-narrowing intent explicit with clearer names and a parenthesized mapped-type condition.

I also changed the narrowing map so same-type matches and structurally unrelated matches fall back to boolean. That covers the HTMLDivElement case you pointed out, so HTMLDivElement.matches("div") stays boolean and HTMLDivElement.matches("object") no longer gets a bogus type-guard path.

I added fixture coverage for the original false-branch regression, HTMLElement narrowing to HTMLDivElement, HTMLDivElement.matches("div") staying boolean, and HTMLDivElement.matches("object") not narrowing to HTMLObjectElement.

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.

DOM: Element#matches() incorrectly narrows types

2 participants