Skip to content

fix: correct return type of document.getElementById from HTMLElement | null to Element | null to account for more general situations like SVGElement #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .clinerules
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Project Structure

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.

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.

# Restrictions

## Original TypeScript type definitions

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.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- fix: correct return type of `document.getElementById` from `HTMLElement | null` to `Element | null` to account for more general situations like `SVGElement`

## v2.10.1

- fix: more generic types for `Promise.then` and `Promise.catch` (https://github.com/uhyo/better-typescript-lib/pull/60)
Expand Down
31 changes: 28 additions & 3 deletions build/logic/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,11 @@ function isPartialReplacement(
const rhc = replacementDecl.heritageClauses;
if (
interfaceDecl.heritageClauses.some((heritageClause, index) => {
return (
heritageClause.getFullText(originalFile) !==
rhc[index].getFullText(betterFile)
return !heritageClauseEquals(
heritageClause,
rhc[index],
originalFile,
betterFile,
);
})
) {
Expand All @@ -352,6 +354,29 @@ function isPartialReplacement(
return true;
}

function heritageClauseEquals(
left: ts.HeritageClause,
right: ts.HeritageClause,
leftSourceFile: ts.SourceFile,
rightSourceFile: ts.SourceFile,
): boolean {
if (left.token !== right.token) {
return false;
}
if (left.types.length !== right.types.length) {
return false;
}
for (let i = 0; i < left.types.length; i++) {
if (
left.types[i].getFullText(leftSourceFile).trim() !==
right.types[i].getFullText(rightSourceFile).trim()
) {
return false;
}
}
return true;
}

/**
* Print an interface declaration where members may be from
* mixed source files.
Expand Down
22 changes: 22 additions & 0 deletions docs/diff/dom.generated.d.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,28 @@ Index: dom.generated.d.ts
}

declare var CustomStateSet: {
@@ -8375,9 +8380,9 @@
/**
* Returns a reference to the first object with the specified value of the ID attribute.
* @param elementId String that specifies the ID value.
*/
- getElementById(elementId: string): HTMLElement | null;
+ getElementById(elementId: string): Element | null;
/**
* 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.
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/Document/getElementsByClassName)
@@ -8563,9 +8568,9 @@
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DocumentFragment)
*/
interface DocumentFragment extends Node, NonElementParentNode, ParentNode {
readonly ownerDocument: Document;
- getElementById(elementId: string): HTMLElement | null;
+ getElementById(elementId: string): Element | null;
}

declare var DocumentFragment: {
prototype: DocumentFragment;
@@ -9378,11 +9383,11 @@
};

Expand Down
10 changes: 8 additions & 2 deletions generated/lib.dom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8386,7 +8386,7 @@ interface Document
* Returns a reference to the first object with the specified value of the ID attribute.
* @param elementId String that specifies the ID value.
*/
getElementById(elementId: string): HTMLElement | null;
getElementById(elementId: string): Element | null;
/**
* 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.
*
Expand Down Expand Up @@ -8559,6 +8559,11 @@ interface Document
options?: boolean | EventListenerOptions,
): void;
}
// /**
// * Returns a reference to the first object with the specified value of the ID attribute.
// * @param elementId String that specifies the ID value.
// */
// getElementById(elementId: string): HTMLElement | null;

declare var Document: {
prototype: Document;
Expand All @@ -8574,8 +8579,9 @@ declare var Document: {
*/
interface DocumentFragment extends Node, NonElementParentNode, ParentNode {
readonly ownerDocument: Document;
getElementById(elementId: string): HTMLElement | null;
getElementById(elementId: string): Element | null;
}
// getElementById(elementId: string): HTMLElement | null;

declare var DocumentFragment: {
prototype: DocumentFragment;
Expand Down
19 changes: 19 additions & 0 deletions lib/lib.dom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,22 @@ interface CustomStateSet {
thisArg?: This,
): void;
}

interface Document
extends Node,
DocumentOrShadowRoot,
FontFaceSource,
GlobalEventHandlers,
NonElementParentNode,
ParentNode,
XPathEvaluatorBase {
/**
* Returns a reference to the first object with the specified value of the ID attribute.
* @param elementId String that specifies the ID value.
*/
getElementById(elementId: string): Element | null;
}

interface DocumentFragment extends Node, NonElementParentNode, ParentNode {
getElementById(elementId: string): Element | null;
}
13 changes: 13 additions & 0 deletions tests/src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,16 @@ const test = async (url: string) => {
// @ts-expect-error
item.append("a");
}

// document.getElementById
{
const element = document.getElementById("test");
expectType<Element | null>(element);

// Verify that the return type is not specifically HTMLElement
expectNotType<HTMLElement | null>(element);

// Verify that SVGElement is assignable to the return type (without type assertion)
const svgElement: SVGElement = {} as SVGElement;
const elementOrNull: typeof element = svgElement;
}