TypeScript-DOM-lib-generator icon indicating copy to clipboard operation
TypeScript-DOM-lib-generator copied to clipboard

Accept type argument to getElementById

Open Sainan opened this issue 3 months ago • 14 comments

This provides an alternative to the common pattern of document.getElementById("...") as HTMLWhateverElement which casts away the possibility of null, consequently hiding a potential type error.

Sainan avatar Sep 09 '25 13:09 Sainan

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.

github-actions[bot] avatar Sep 09 '25 13:09 github-actions[bot]

Might be worth pointing out npm run baseline-accept in the "contribution guidelines" 🍞

Sainan avatar Sep 09 '25 13:09 Sainan

I think the guideline has been that, if you are not actually using the type parameter in the signature then it should not exist, and rather you should cast from the return type. @jakebailey, is that still the case?

saschanaz avatar Sep 09 '25 13:09 saschanaz

Yes, this effectively a type assertion in disguise. It's not good to have this kind of signature. An explicit assertion is much preferred.

jakebailey avatar Sep 09 '25 16:09 jakebailey

I understand the risks but I don't think this is a good idea.

(If we were to have something like this, it'd at least have to be T extends ... = ...

jakebailey avatar Sep 09 '25 16:09 jakebailey

My case for it: querySelector and querySelectorAll have it. It is very common for the T to be a type assertion (e.g. JSON.parse)

Also updated to require that T extends HTMLElement

Sainan avatar Sep 09 '25 16:09 Sainan

My case for it: querySelector and querySelectorAll have it.

I have no idea why these have it, I doubt we would choose to do so if the same decision were made today. It's patently unsafe and hides the type assertion from tools that intend to find unsafety.

It is very common for the T to be a type assertion (e.g. JSON.parse)

JSON.parse doesn't do this? Unless you're talking about third party patches?

jakebailey avatar Sep 09 '25 16:09 jakebailey

I have no idea why these have it, I doubt we would choose to do so if the same decision were made today. It's patently unsafe and hides the type assertion from tools that intend to find unsafety.

tbh I have no idea about the tooling landscape here (my version of Vite is called PHP) but to me a ! seems more explicit than the as ... just casting away the | null

JSON.parse doesn't do this? Unless you're talking about third party patches?

My mistake, the typing I got is from json-with-bigint

Sainan avatar Sep 09 '25 16:09 Sainan

querySelector has it because if you do querySelector("input") then you know it's going to be HTMLInputElement. Not much for getElementById.

saschanaz avatar Sep 09 '25 17:09 saschanaz

I pinky promise I know what my .html/.php file looks like 😄

Sainan avatar Sep 09 '25 17:09 Sainan

querySelector has overloads that have nice return types, but then they end with:

querySelector<E extends Element = Element>(selectors: string): E | null;
querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;

I'm not sure if that's because it's required to make the overload checks happy, but I don't think so 😦

jakebailey avatar Sep 09 '25 17:09 jakebailey

I pinky promise I know what my .html/.php file looks like 😄

That same argument could apply to all of TS 😄

"I know better than the compiler" is supposed to be casts and !, not type vars... But I understand the tension and the nullness problem here.

jakebailey avatar Sep 09 '25 17:09 jakebailey

Example code for your consideration:

image image

The relevant part:

document.getElementById("filter-" + opt) as HTMLInputElement | null

Could be written currently as:

document.querySelector<HTMLInputElement>("#filter-" + opt)

But with this PR;

document.getElementById<HTMLInputElement>("filter-" + opt)

Sainan avatar Sep 09 '25 17:09 Sainan

For me the added null is a very good thing, In our codebase are some as HTMLElement after getElementById where the junior devs forgot (and therefor dropped) the null.

With this PR we could migrate to the generics and will get the null for free.

HolgerJeromin avatar Sep 09 '25 20:09 HolgerJeromin