Skip to content

Commit 62a9488

Browse files
committed
Fix Element.matches strict narrowing
1 parent 8273e37 commit 62a9488

6 files changed

Lines changed: 65 additions & 13 deletions

File tree

baselines/dom.generated.d.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14051,9 +14051,12 @@ interface Element extends Node, ARIAMixin, Animatable, ChildNode, NonDocumentTyp
1405114051
*
1405214052
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/matches)
1405314053
*/
14054-
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): this is HTMLElementTagNameMap[K];
14055-
matches<K extends keyof SVGElementTagNameMap>(selectors: K): this is SVGElementTagNameMap[K];
14056-
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): this is MathMLElementTagNameMap[K];
14054+
matches<K extends keyof ElementMatchesMap<HTMLElementTagNameMap, this>>(selectors: K): this is Extract<HTMLElementTagNameMap[K], this>;
14055+
matches<K extends keyof ElementMatchesMap<SVGElementTagNameMap, this>>(selectors: K): this is Extract<SVGElementTagNameMap[K], this>;
14056+
matches<K extends keyof ElementMatchesMap<MathMLElementTagNameMap, this>>(selectors: K): this is Extract<MathMLElementTagNameMap[K], this>;
14057+
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): boolean;
14058+
matches<K extends keyof SVGElementTagNameMap>(selectors: K): boolean;
14059+
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): boolean;
1405714060
matches(selectors: string): boolean;
1405814061
/**
1405914062
* The **`releasePointerCapture()`** method of the Element interface releases (stops) pointer capture that was previously set for a specific (PointerEvent) pointer.
@@ -43880,6 +43883,8 @@ interface MathMLElementTagNameMap {
4388043883
"semantics": MathMLElement;
4388143884
}
4388243885

43886+
type ElementMatchesMap<T, U> = { [K in keyof T as T[K] extends U ? U extends T[K] ? never : K : never]: T[K] };
43887+
4388343888
/** @deprecated Directly use HTMLElementTagNameMap or SVGElementTagNameMap as appropriate, instead. */
4388443889
type ElementTagNameMap = HTMLElementTagNameMap & Pick<SVGElementTagNameMap, Exclude<keyof SVGElementTagNameMap, keyof HTMLElementTagNameMap>>;
4388543890

baselines/ts5.5/dom.generated.d.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14038,9 +14038,12 @@ interface Element extends Node, ARIAMixin, Animatable, ChildNode, NonDocumentTyp
1403814038
*
1403914039
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/matches)
1404014040
*/
14041-
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): this is HTMLElementTagNameMap[K];
14042-
matches<K extends keyof SVGElementTagNameMap>(selectors: K): this is SVGElementTagNameMap[K];
14043-
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): this is MathMLElementTagNameMap[K];
14041+
matches<K extends keyof ElementMatchesMap<HTMLElementTagNameMap, this>>(selectors: K): this is Extract<HTMLElementTagNameMap[K], this>;
14042+
matches<K extends keyof ElementMatchesMap<SVGElementTagNameMap, this>>(selectors: K): this is Extract<SVGElementTagNameMap[K], this>;
14043+
matches<K extends keyof ElementMatchesMap<MathMLElementTagNameMap, this>>(selectors: K): this is Extract<MathMLElementTagNameMap[K], this>;
14044+
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): boolean;
14045+
matches<K extends keyof SVGElementTagNameMap>(selectors: K): boolean;
14046+
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): boolean;
1404414047
matches(selectors: string): boolean;
1404514048
/**
1404614049
* The **`releasePointerCapture()`** method of the Element interface releases (stops) pointer capture that was previously set for a specific (PointerEvent) pointer.
@@ -43854,6 +43857,8 @@ interface MathMLElementTagNameMap {
4385443857
"semantics": MathMLElement;
4385543858
}
4385643859

43860+
type ElementMatchesMap<T, U> = { [K in keyof T as T[K] extends U ? U extends T[K] ? never : K : never]: T[K] };
43861+
4385743862
/** @deprecated Directly use HTMLElementTagNameMap or SVGElementTagNameMap as appropriate, instead. */
4385843863
type ElementTagNameMap = HTMLElementTagNameMap & Pick<SVGElementTagNameMap, Exclude<keyof SVGElementTagNameMap, keyof HTMLElementTagNameMap>>;
4385943864

baselines/ts5.6/dom.generated.d.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14048,9 +14048,12 @@ interface Element extends Node, ARIAMixin, Animatable, ChildNode, NonDocumentTyp
1404814048
*
1404914049
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/matches)
1405014050
*/
14051-
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): this is HTMLElementTagNameMap[K];
14052-
matches<K extends keyof SVGElementTagNameMap>(selectors: K): this is SVGElementTagNameMap[K];
14053-
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): this is MathMLElementTagNameMap[K];
14051+
matches<K extends keyof ElementMatchesMap<HTMLElementTagNameMap, this>>(selectors: K): this is Extract<HTMLElementTagNameMap[K], this>;
14052+
matches<K extends keyof ElementMatchesMap<SVGElementTagNameMap, this>>(selectors: K): this is Extract<SVGElementTagNameMap[K], this>;
14053+
matches<K extends keyof ElementMatchesMap<MathMLElementTagNameMap, this>>(selectors: K): this is Extract<MathMLElementTagNameMap[K], this>;
14054+
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): boolean;
14055+
matches<K extends keyof SVGElementTagNameMap>(selectors: K): boolean;
14056+
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): boolean;
1405414057
matches(selectors: string): boolean;
1405514058
/**
1405614059
* The **`releasePointerCapture()`** method of the Element interface releases (stops) pointer capture that was previously set for a specific (PointerEvent) pointer.
@@ -43877,6 +43880,8 @@ interface MathMLElementTagNameMap {
4387743880
"semantics": MathMLElement;
4387843881
}
4387943882

43883+
type ElementMatchesMap<T, U> = { [K in keyof T as T[K] extends U ? U extends T[K] ? never : K : never]: T[K] };
43884+
4388043885
/** @deprecated Directly use HTMLElementTagNameMap or SVGElementTagNameMap as appropriate, instead. */
4388143886
type ElementTagNameMap = HTMLElementTagNameMap & Pick<SVGElementTagNameMap, Exclude<keyof SVGElementTagNameMap, keyof HTMLElementTagNameMap>>;
4388243887

baselines/ts5.9/dom.generated.d.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14048,9 +14048,12 @@ interface Element extends Node, ARIAMixin, Animatable, ChildNode, NonDocumentTyp
1404814048
*
1404914049
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/matches)
1405014050
*/
14051-
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): this is HTMLElementTagNameMap[K];
14052-
matches<K extends keyof SVGElementTagNameMap>(selectors: K): this is SVGElementTagNameMap[K];
14053-
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): this is MathMLElementTagNameMap[K];
14051+
matches<K extends keyof ElementMatchesMap<HTMLElementTagNameMap, this>>(selectors: K): this is Extract<HTMLElementTagNameMap[K], this>;
14052+
matches<K extends keyof ElementMatchesMap<SVGElementTagNameMap, this>>(selectors: K): this is Extract<SVGElementTagNameMap[K], this>;
14053+
matches<K extends keyof ElementMatchesMap<MathMLElementTagNameMap, this>>(selectors: K): this is Extract<MathMLElementTagNameMap[K], this>;
14054+
matches<K extends keyof HTMLElementTagNameMap>(selectors: K): boolean;
14055+
matches<K extends keyof SVGElementTagNameMap>(selectors: K): boolean;
14056+
matches<K extends keyof MathMLElementTagNameMap>(selectors: K): boolean;
1405414057
matches(selectors: string): boolean;
1405514058
/**
1405614059
* The **`releasePointerCapture()`** method of the Element interface releases (stops) pointer capture that was previously set for a specific (PointerEvent) pointer.
@@ -43877,6 +43880,8 @@ interface MathMLElementTagNameMap {
4387743880
"semantics": MathMLElement;
4387843881
}
4387943882

43883+
type ElementMatchesMap<T, U> = { [K in keyof T as T[K] extends U ? U extends T[K] ? never : K : never]: T[K] };
43884+
4388043885
/** @deprecated Directly use HTMLElementTagNameMap or SVGElementTagNameMap as appropriate, instead. */
4388143886
type ElementTagNameMap = HTMLElementTagNameMap & Pick<SVGElementTagNameMap, Exclude<keyof SVGElementTagNameMap, keyof HTMLElementTagNameMap>>;
4388243887

src/build/emitter.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,12 @@ export function emitWebIdl(
645645
const paramName = m.signature[0].param![0].name;
646646
for (const mapName of tagNameMapNames) {
647647
printer.printLine(
648-
`matches<K extends keyof ${mapName}>(${paramName}: K): this is ${mapName}[K];`,
648+
`matches<K extends keyof ElementMatchesMap<${mapName}, this>>(${paramName}: K): this is Extract<${mapName}[K], this>;`,
649+
);
650+
}
651+
for (const mapName of tagNameMapNames) {
652+
printer.printLine(
653+
`matches<K extends keyof ${mapName}>(${paramName}: K): boolean;`,
649654
);
650655
}
651656
printer.printLine(`matches(${paramName}: string): boolean;`);
@@ -715,6 +720,13 @@ export function emitWebIdl(
715720
printer.printLine("");
716721
}
717722

723+
function emitElementMatchesMap() {
724+
printer.printLine(
725+
"type ElementMatchesMap<T, U> = { [K in keyof T as T[K] extends U ? U extends T[K] ? never : K : never]: T[K] };",
726+
);
727+
printer.printLine("");
728+
}
729+
718730
/// Emit overloads for the createEvent method
719731
function emitCreateEventOverloads(m: Browser.Method) {
720732
if (matchParamMethodSignature(m, "createEvent", "Event", "string")) {
@@ -1664,6 +1676,7 @@ export function emitWebIdl(
16641676
"MathMLElementTagNameMap",
16651677
tagNameToEleName.mathMLResult,
16661678
);
1679+
emitElementMatchesMap();
16671680
emitDeprecatedHTMLOrSVGElementTagNameMap();
16681681
emitNamedConstructors();
16691682
}

unittests/files/matches.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
declare const element: Element;
2+
declare const htmlElement: HTMLElement;
3+
declare const htmlTableCellElement: HTMLTableCellElement;
4+
5+
if (element.matches("dt")) {
6+
const narrowed: HTMLElement = element;
7+
}
8+
9+
if (!htmlElement.matches("dt")) {
10+
htmlElement.id;
11+
}
12+
13+
if (htmlElement.matches("td")) {
14+
const narrowed: HTMLTableCellElement = htmlElement;
15+
}
16+
17+
if (!htmlTableCellElement.matches("th")) {
18+
htmlTableCellElement.id;
19+
}

0 commit comments

Comments
 (0)