Skip to content

Conversation

@ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented Jan 15, 2026

In this PR:

  • In JS files, permit expando properties to be defined with Object.defineProperty.
  • In JS files, permit CommonJS exports to be defined with Object.defineProperty applied to exports or module.exports.
  • Fix circularity errors and unwanted implicit-any errors by deferring resolution of expando object property types.
  • Fix error on typeof module.exports when used to define types in JSDoc comments in JS files.
  • Cache creation of CommonJS module symbols in getModuleSymbol.
  • Don't emit CommonJSExport nodes in JS files (those are synthetic reparsed nodes intended just for checking).
  • Refactor declaration emit to support CommonJS exports defined with Object.defineProperty.
  • Port Cache started nonexistent property error checks to prevent reentrancy in the check TypeScript#60683 to fix stack overflow in test.
  • Support chained assignments in reparsing.
  • Revised handling of CommonJS assignment declarations with undefined initializers.

The revised handling of CommonJS assignment declarations now ignores undefined initializers when they occur as the initial assignment declaration of an export with multiple declarations. This provides for the common pattern of first initializing CommonJS exports to undefined and then subsequently assigning the actual exported value.

Fixes #2527.

// [`prop`]: 1
// let obj = {};
//
// Object.defineProperty(obj, `prop`, { value: 0 });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like Strada's rename would also update the defineProperty. Not sure how possible that is now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now fixed, except initiating a rename in a string literal doesn't work, but that's a general issue for Corsa that we should fix in a separate PR.

Comment on lines +174 to +188
+ y: typeof module.exports.b;
+ ~~~~~~
+!!! error TS2580: Cannot find name 'module'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.
+ }): void;
+ declare const _exported_6: typeof hh;
+ export { _exported_6 as "h" };
+ declare const _exported_7: () => void;
+ export { _exported_7 as "i" };
+ declare const _exported_8: () => void;
+ export { _exported_8 as "ii" };
+ declare const _exported_9: () => void;
+ export { _exported_9 as "jj" };
+ declare const _exported_10: () => void;
+ export { _exported_10 as "j" };
+ No newline at end of file
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The exports here seem to be causing module to not exist, or something? Definitely odd.

Copy link
Member Author

@ahejlsberg ahejlsberg Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a declaration emit issue. The old declaration emitter wouldn't attempt to reference module.exports (it's only a CommonJS thing and can't be referenced in a .d.ts file), but instead would inline the actual type.

Comment on lines +9 to +11
+ var chrome = {}
+ Object.defineProperty(chrome, 'devtools', { value: {}, enumerable: true })
+ chrome.devtools.inspectedWindow = {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this is consistentish, but it is weird that if I write:

var chrome = {}
chrome.devtools = {}
chrome.devtools.inspectedWindow = {}

You get an error on the second line too, so expandos are sort of a way around a check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably fix this.

Copy link
Member Author

@ahejlsberg ahejlsberg Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works in a JS file (but not in a TS file):

var chrome = {}
chrome.devtools = {}
chrome.devtools.inspectedWindow = {}

So you could argue that using Object.defineProperty for the middle one should work too. Which is the case in Strada.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, having expando objects that are rooted in Object.defineProperty seems like a totally esoteric feature. I'm going to hold off on implementing that.

Copilot AI review requested due to automatic review settings January 15, 2026 04:09
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for Object.defineProperty in JavaScript files, specifically for:

  • Expando properties (properties added to regular objects)
  • CommonJS exports defined via Object.defineProperty on exports or module.exports

The changes fix circularity errors and implicit-any errors by deferring type resolution, fix errors when using typeof module.exports in JSDoc, cache module symbols, prevent emission of CommonJSExport nodes in JS files, and refactor declaration emit.

Changes:

  • Support for Object.defineProperty to define CommonJS exports and expando properties in JS files
  • Fixes for error handling around typeof module.exports in JSDoc comments
  • Removal of erroneous errors about missing module and exports in JS files
  • Improved type inference for properties defined with Object.defineProperty

Reviewed changes

Copilot reviewed 149 out of 149 changed files in this pull request and generated no comments.

Show a summary per file
File Description
checkExportsObjectAssignProperty.*.diff Tests now correctly recognize exports defined via Object.defineProperty, removing false positive errors about undefined module/exports
assignmentToVoidZero*.diff Fixes type handling for void 0 assignments
typeFromPropertyAssignment*.diff Improved symbol and type resolution for property assignments
jsDeclarations*.diff Removes spurious export var statements in emitted JS files
checkOtherObjectAssignProperty.*.diff Properly recognizes properties defined via Object.defineProperty on exports
checkObjectDefineProperty.*.diff Correctly infers types from Object.defineProperty calls
defaultPropertyAssignedClassWithPrototype.*.diff Better type tracking for expando properties
nodeModulesCJSEmit*.diff Removes incorrect export var statements in CommonJS emit
Various other conformance tests Fixes related to CommonJS export handling

@jakebailey
Copy link
Member

Are you planning on putting more into this PR or is it ready?

@ahejlsberg
Copy link
Member Author

Are you planning on putting more into this PR or is it ready?

The PR is ready now. The tests reveal issues in declaration emit, namely:

  • Use of typeof module.exports.xxx (which isn't permitted in .ts files),
  • Emitting multiple export declare var foo: Foo for multiple assignments to exports.foo, and
  • Emitting export declare var foo: Foo instead of export const foo: Foo for CommonJS exports.

We can address these in separate PRs.

@ahejlsberg ahejlsberg added this pull request to the merge queue Jan 17, 2026
Merged via the queue into main with commit ebba558 Jan 17, 2026
21 checks passed
@ahejlsberg ahejlsberg deleted the object-define-property branch January 17, 2026 17:08
github-merge-queue bot pushed a commit that referenced this pull request Jan 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tsgo incorrectly emits both ESM and CJS exports when compiling .js files containing exports.xxx statements

3 participants