Skip to content

Conversation

@ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented Nov 1, 2025

In this PR:

  • Only export @typedef type aliases when they occur in modules (i.e. don't export in script files).
  • Mark reparsed export assignment nodes as a TypeScript construct such that they're omitted from emitted .js.
  • Fix LSP crash by adding missing case for ast.KindJSTypeAliasDeclaration.

Most of the baseline changes result from not marking script files as modules and no longer including invalid export = in emitted .js files.

Fixes #1988.

@ahejlsberg ahejlsberg requested review from Copilot, jakebailey and sandersn and removed request for Copilot November 1, 2025 22:43
Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

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

Seems like a net improvement; reading over the diffs we of course still have more JS/JSDoc bugs to address in the future...

Comment on lines 15 to 18
// === /b.js ===
// /** @type {import('./a').[|Foo|]} */
// const x = 0;

Copy link
Member

Choose a reason for hiding this comment

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

This seems like a regression, maybe? a.js theoretically is a module because it has module.exports in it, I think, but now we are losing that export and therefore the reference.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, that's an oversight. We need to handle the implied export of @typedef in find-all-references.

@@ -10,13 +10,12 @@
function funky(declaration) {
return false;
}
+export = donkey;
module.exports = donkey;
+export var funky = funky;
Copy link
Member

Choose a reason for hiding this comment

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

Not your bug but definitely an interesting one.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is actually a bug fix I made part of this PR. We now mark reparsed export assignment nodes as a TypeScript construct such that they're properly omitted from the emitted .js.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, right, I was just commenting on the remaining diff in the file.

Copy link
Member Author

Choose a reason for hiding this comment

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

Got it. Definitely funky!

Copilot AI review requested due to automatic review settings November 2, 2025 14:47
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 fixes the handling of JSDoc @typedef declarations in CommonJS/external modules. Previously, @typedef declarations were being marked as exported during parsing. The fix moves this logic to later stages (declaration emit and binder), ensuring proper scoping and type visibility.

Key Changes

  • Removed premature export modifier addition during parsing of @typedef tags
  • Added logic in declaration transformer to mark @typedef as exported when in external modules
  • Updated binder, checker, and language server to recognize @typedef as implicitly exported in modules
  • Fixed export assignment to properly flag TypeScript subtree facts

Reviewed Changes

Copilot reviewed 155 out of 155 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
internal/parser/reparser.go Removes explicit export modifier from @typedef parsing
internal/transformers/declarations/transform.go Adds export flag for @typedef in external modules during emission
internal/binder/binder.go Treats @typedef as having export modifier in modules
internal/checker/emitresolver.go Makes @typedef visible in external modules
internal/checker/checker.go Adds @typedef to allowed class member kinds
internal/ls/importTracker.go Updates export detection to handle @typedef
internal/ast/ast.go Fixes ExportAssignment subtree facts and adds @typedef to write access declarations
testdata/baselines/* Updated test baselines showing corrected behavior

modifiers := p.newModifierList(export.Loc, p.nodeSlicePool.NewSlice1(export))

typeAlias := p.factory.NewJSTypeAliasDeclaration(modifiers, p.factory.DeepCloneReparse(tag.AsJSDocTypedefTag().Name()), nil, nil)
typeAlias := p.factory.NewJSTypeAliasDeclaration(nil, p.factory.DeepCloneReparse(tag.AsJSDocTypedefTag().Name()), nil, nil)
Copy link

Copilot AI Nov 2, 2025

Choose a reason for hiding this comment

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

According to custom coding guideline 1000000, the comment on line 73 states '!!! Don't mark typedefs as exported if they are not in a module'. However, the fix now relies on later stages to determine if the typedef is in a module. Consider updating this comment to clarify that export modifiers are added during declaration transformation based on module context, not during parsing.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 1585 to 1590
if ast.IsJSTypeAliasDeclaration(node) && parentIsFile && ast.IsExternalOrCommonJSModule(node.Parent.AsSourceFile()) {
additions |= ast.ModifierFlagsExport
}
return maskModifierFlags(tx.host, node, mask, additions)
}

Copy link

Copilot AI Nov 2, 2025

Choose a reason for hiding this comment

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

This logic for determining whether a JSTypeAliasDeclaration should be exported is duplicated in multiple locations (binder.go line 383, emitresolver.go line 164, importTracker.go line 575). Consider extracting this into a shared helper function to improve maintainability and ensure consistency.

Suggested change
if ast.IsJSTypeAliasDeclaration(node) && parentIsFile && ast.IsExternalOrCommonJSModule(node.Parent.AsSourceFile()) {
additions |= ast.ModifierFlagsExport
}
return maskModifierFlags(tx.host, node, mask, additions)
}
if shouldExportJSTypeAliasDeclaration(node) {
additions |= ast.ModifierFlagsExport
}
return maskModifierFlags(tx.host, node, mask, additions)
}

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

Is this worth addressing if we are actually copy and pasting this code?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yup, done.

@ahejlsberg ahejlsberg enabled auto-merge November 2, 2025 16:46
@ahejlsberg ahejlsberg added this pull request to the merge queue Nov 2, 2025
Merged via the queue into main with commit 82039b6 Nov 2, 2025
22 checks passed
@ahejlsberg ahejlsberg deleted the fix-1988 branch November 2, 2025 18:58
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.

Reparsed @typedef turns into export type even in script files

3 participants