You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Use overloads to preserve legacy parameter names when combined with modern Options objects #2470
I'm a member of the TypeScript/JavaScript team at Google. We are currently working on a lint rule that enforces that call-site parameter comments /* likeThis= */ match their declarations.
The problem we are seeing is that several DOM methods have transitioned to using an options bag for certain arguments. In the generated dom.generated.d.ts, these options are collapsed into a single signature using a union type. When this happens, the specific semantic name of the boolean (e.g., useCapture or selfOnly) is replaced by a generic name.
This breaks developer intent for:
IDE Hover Tips: Users see options: boolean | ImportNodeOptions instead of the descriptive boolean name.
Call-site Documentation: Lint rules and internal style guides that enforce parameter comments (e.g., /* selfOnly= */ true) fail because the AST reflects the name options, not selfOnly.
Examples
document.importNode
The spec moved from deep: boolean to an options object where the boolean equivalent (inverted) is now selfOnly.
Some other effected methods include removeEventListener and scrollIntoView
Proposed Solution
I propose that the generator (or manual overrides) split these into explicit overloads to preserve the legacy parameter names alongside the modern object-based signature. I understand these definitions are derived from Web IDL. However, since the TypeScript lib aims to provide the best possible developer experience, adding these overloads to overridingTypes.json (or the equivalent generation logic) would significantly improve the clarity of codebases that still rely on the boolean flags for performance or brevity.
I propose that the generator (or manual overrides) split these into explicit overloads to preserve the legacy parameter names alongside the modern object-based signature.
Note that this can end up being a breaking change. For example, in both cases you've given, it's impossible to call the overloaded version with a union of bool | Options as neither accept both.
Hi Jake! What I failed to mention was that we would still keep the union-based signature. Could we avoid breakage by keeping the union-based signature as the final declaration while providing a specific overload for the boolean case above it? We really only need the overload with the old api declaration anyway.
Mainly, I'm trying to understand how big of a change this would be, since the fewer special cases we have the better, or if there is a clear pattern in the upstream spec for this. (Maybe these are already handwritten only.)
As far as I can tell, there are not many cases. I've seen it 4 times. Though I imagine that over time there will be other apis that follow this pattern.
After the May 4th 2026 TypeScript Friends meeting, the consensus was that instead of doing overloads, we would change the parameter names to aOrB for cases where this happens.
Activity
jakebailey commentedon Apr 20, 2026
Note that this can end up being a breaking change. For example, in both cases you've given, it's impossible to call the overloaded version with a union of
bool | Optionsas neither accept both.spkapust commentedon Apr 20, 2026
Hi Jake! What I failed to mention was that we would still keep the union-based signature. Could we avoid breakage by keeping the union-based signature as the final declaration while providing a specific overload for the boolean case above it? We really only need the overload with the old api declaration anyway.
jakebailey commentedon Apr 20, 2026
How many of these cases are there?
spkapust commentedon Apr 20, 2026
By cases do you mean how many methods? Or how many times are we seeing this issue come up when we try to apply our lint?
For the former I mainly see two, importNode and addEventListener. I can give an exact number if you need it.
For the latter it's on the order of hundreds.
jakebailey commentedon Apr 20, 2026
Mainly, I'm trying to understand how big of a change this would be, since the fewer special cases we have the better, or if there is a clear pattern in the upstream spec for this. (Maybe these are already handwritten only.)
spkapust commentedon Apr 20, 2026
As far as I can tell, there are not many cases. I've seen it 4 times. Though I imagine that over time there will be other apis that follow this pattern.
addEventListenerspecremoveEventListenerspecimportNodespecscrollIntoViewspecspkapust commentedon May 4, 2026
After the May 4th 2026 TypeScript Friends meeting, the consensus was that instead of doing overloads, we would change the parameter names to
aOrBfor cases where this happens.e.g.
importNode<T extends Node>(importedNode: T, options?: ImportNodeOptions | boolean): T;becomes
importNode<T extends Node>(importedNode: T, optionsOrDeep?: ImportNodeOptions | boolean): T;spkapust commentedon May 4, 2026
Let me know if you want me to put in a PR or if this something easier done on your end.