Skip to content

Commit d4a80c2

Browse files
authored
fix: correct return type of document.getElementById from HTMLElement | null to Element | null to account for more general situations like SVGElement (#65)
* fix: improve isPartialReplacement logic * fix: correct return type of `document.getElementById` from `HTMLElement | null` to `Element | null` to account for more general situations like `SVGElement`
1 parent 16bb86e commit d4a80c2

File tree

7 files changed

+105
-5
lines changed

7 files changed

+105
-5
lines changed

.clinerules

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Project Structure
2+
3+
This project, better-ts-lib, is a project that provides an alternative to TypeScript's standard library. The main ingredients of this project are in the `lib/` directory which contains incremental differences to the standard library.
4+
5+
This project is structured differently from ordinary TypeScript projects. Carefully read CONTRIBUTING.md to understand how to modify the type definitions, how to build the project, and how to test the project.
6+
7+
# Restrictions
8+
9+
## Original TypeScript type definitions
10+
11+
Please do not try to read the original type definitions from TypeScript. Those may be very large and you cannot read it. Instead, ask the user to provide relevant code snippets from the original type definitions.

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- fix: correct return type of `document.getElementById` from `HTMLElement | null` to `Element | null` to account for more general situations like `SVGElement`
6+
37
## v2.10.1
48

59
- fix: more generic types for `Promise.then` and `Promise.catch` (https://github.com/uhyo/better-typescript-lib/pull/60)

build/logic/generate.ts

+28-3
Original file line numberDiff line numberDiff line change
@@ -335,9 +335,11 @@ function isPartialReplacement(
335335
const rhc = replacementDecl.heritageClauses;
336336
if (
337337
interfaceDecl.heritageClauses.some((heritageClause, index) => {
338-
return (
339-
heritageClause.getFullText(originalFile) !==
340-
rhc[index].getFullText(betterFile)
338+
return !heritageClauseEquals(
339+
heritageClause,
340+
rhc[index],
341+
originalFile,
342+
betterFile,
341343
);
342344
})
343345
) {
@@ -352,6 +354,29 @@ function isPartialReplacement(
352354
return true;
353355
}
354356

357+
function heritageClauseEquals(
358+
left: ts.HeritageClause,
359+
right: ts.HeritageClause,
360+
leftSourceFile: ts.SourceFile,
361+
rightSourceFile: ts.SourceFile,
362+
): boolean {
363+
if (left.token !== right.token) {
364+
return false;
365+
}
366+
if (left.types.length !== right.types.length) {
367+
return false;
368+
}
369+
for (let i = 0; i < left.types.length; i++) {
370+
if (
371+
left.types[i].getFullText(leftSourceFile).trim() !==
372+
right.types[i].getFullText(rightSourceFile).trim()
373+
) {
374+
return false;
375+
}
376+
}
377+
return true;
378+
}
379+
355380
/**
356381
* Print an interface declaration where members may be from
357382
* mixed source files.

docs/diff/dom.generated.d.ts.md

+22
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,28 @@ Index: dom.generated.d.ts
5151
}
5252

5353
declare var CustomStateSet: {
54+
@@ -8375,9 +8380,9 @@
55+
/**
56+
* Returns a reference to the first object with the specified value of the ID attribute.
57+
* @param elementId String that specifies the ID value.
58+
*/
59+
- getElementById(elementId: string): HTMLElement | null;
60+
+ getElementById(elementId: string): Element | null;
61+
/**
62+
* Returns a HTMLCollection of the elements in the object on which the method was invoked (a document or an element) that have all the classes given by classNames. The classNames argument is interpreted as a space-separated list of classes.
63+
*
64+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementsByClassName)
65+
@@ -8563,9 +8568,9 @@
66+
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DocumentFragment)
67+
*/
68+
interface DocumentFragment extends Node, NonElementParentNode, ParentNode {
69+
readonly ownerDocument: Document;
70+
- getElementById(elementId: string): HTMLElement | null;
71+
+ getElementById(elementId: string): Element | null;
72+
}
73+
74+
declare var DocumentFragment: {
75+
prototype: DocumentFragment;
5476
@@ -9378,11 +9383,11 @@
5577
};
5678

generated/lib.dom.d.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -8386,7 +8386,7 @@ interface Document
83868386
* Returns a reference to the first object with the specified value of the ID attribute.
83878387
* @param elementId String that specifies the ID value.
83888388
*/
8389-
getElementById(elementId: string): HTMLElement | null;
8389+
getElementById(elementId: string): Element | null;
83908390
/**
83918391
* Returns a HTMLCollection of the elements in the object on which the method was invoked (a document or an element) that have all the classes given by classNames. The classNames argument is interpreted as a space-separated list of classes.
83928392
*
@@ -8559,6 +8559,11 @@ interface Document
85598559
options?: boolean | EventListenerOptions,
85608560
): void;
85618561
}
8562+
// /**
8563+
// * Returns a reference to the first object with the specified value of the ID attribute.
8564+
// * @param elementId String that specifies the ID value.
8565+
// */
8566+
// getElementById(elementId: string): HTMLElement | null;
85628567

85638568
declare var Document: {
85648569
prototype: Document;
@@ -8574,8 +8579,9 @@ declare var Document: {
85748579
*/
85758580
interface DocumentFragment extends Node, NonElementParentNode, ParentNode {
85768581
readonly ownerDocument: Document;
8577-
getElementById(elementId: string): HTMLElement | null;
8582+
getElementById(elementId: string): Element | null;
85788583
}
8584+
// getElementById(elementId: string): HTMLElement | null;
85798585

85808586
declare var DocumentFragment: {
85818587
prototype: DocumentFragment;

lib/lib.dom.d.ts

+19
Original file line numberDiff line numberDiff line change
@@ -229,3 +229,22 @@ interface CustomStateSet {
229229
thisArg?: This,
230230
): void;
231231
}
232+
233+
interface Document
234+
extends Node,
235+
DocumentOrShadowRoot,
236+
FontFaceSource,
237+
GlobalEventHandlers,
238+
NonElementParentNode,
239+
ParentNode,
240+
XPathEvaluatorBase {
241+
/**
242+
* Returns a reference to the first object with the specified value of the ID attribute.
243+
* @param elementId String that specifies the ID value.
244+
*/
245+
getElementById(elementId: string): Element | null;
246+
}
247+
248+
interface DocumentFragment extends Node, NonElementParentNode, ParentNode {
249+
getElementById(elementId: string): Element | null;
250+
}

tests/src/dom.ts

+13
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,16 @@ const test = async (url: string) => {
148148
// @ts-expect-error
149149
item.append("a");
150150
}
151+
152+
// document.getElementById
153+
{
154+
const element = document.getElementById("test");
155+
expectType<Element | null>(element);
156+
157+
// Verify that the return type is not specifically HTMLElement
158+
expectNotType<HTMLElement | null>(element);
159+
160+
// Verify that SVGElement is assignable to the return type (without type assertion)
161+
const svgElement: SVGElement = {} as SVGElement;
162+
const elementOrNull: typeof element = svgElement;
163+
}

0 commit comments

Comments
 (0)