Don't latebind global JS property assignments#61593
Don't latebind global JS property assignments#61593sandersn wants to merge 3 commits intomicrosoft:mainfrom
Conversation
Late binding assumes a parent symbol. Currently, the binder does not check whether an assignment declaration has a parent symbol exists before creating a symbol for late binding. The easiest place to encounter this is with `window` assignments, because global (script) files have no symbol, unlike module files: ```js const X = "X" window[X] = () => 1 ``` It is possible to special-case `window` for late binding similar to the way that it's done for normal binding. But I don't think there's enough value. For now, this PR prevents binding entirely for a dynamic name when there is no parent symbol.
There was a problem hiding this comment.
Pull Request Overview
This PR fixes an issue with latebound global JavaScript property assignments by preventing binding for dynamic names when no parent symbol is present.
- Added an early return in the binder when parentSymbol is undefined in dynamic name assignments
- Updated the test case to verify that global assignments to window properties using dynamic names are not latebound
Reviewed Changes
Copilot reviewed 2 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/cases/conformance/salsa/lateboundWindowToplevelAssignment.ts | Added a test to verify that global dynamic assignments (e.g. window[...] = …) do not create a symbol |
| src/compiler/binder.ts | Added a check for a missing parent symbol before binding dynamic names to prevent late binding in global files |
Files not reviewed (2)
- tests/baselines/reference/lateboundWindowToplevelAssignment.symbols: Language not supported
- tests/baselines/reference/lateboundWindowToplevelAssignment.types: Language not supported
| else if (hasDynamicName(node)) { | ||
| if (!parentSymbol) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
The added check prevents binding for dynamic property assignments when no parent symbol is present, avoiding unintended symbol creation in global files. Consider adding an inline comment here to clarify why binding is skipped in this scenario for future maintainability.
| else if (hasDynamicName(node)) { | |
| if (!parentSymbol) { | |
| return; | |
| } | |
| else if (hasDynamicName(node)) { | |
| // Skip binding for dynamic property assignments when no parent symbol is present. | |
| // This prevents unintended symbol creation in global files, ensuring correctness. | |
| if (!parentSymbol) { | |
| return; | |
| } |
There was a problem hiding this comment.
I don't really agree, but I would change my mind if a human spoke up saying that they do.
|
|
||
| window[UPDATE_MARKER_FUNC](); | ||
| >window[UPDATE_MARKER_FUNC]() : error | ||
| >window[UPDATE_MARKER_FUNC] : error |
There was a problem hiding this comment.
Having type error shows that binding didn't happen.
Late binding assumes a parent symbol. Currently, the binder does not check whether an assignment declaration has a parent symbol exists before creating a symbol for late binding. The easiest place to encounter this is with
windowassignments, because global (script) files have no symbol, unlike module files:It is possible to special-case
windowfor late binding similar to the way that it's done for normal binding, or even any top-level assignment declaration in a global file. But I don't think there's enough value. For now, this PR prevents binding entirely for a dynamic name when there is no parent symbol.Fixes #61583