From 9a11fc546e650d55024c82b9697fea02f6d061b9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 23:17:46 +0000 Subject: [PATCH 1/3] Initial plan From fb16199ed4a75c3af4782f6121a1e48255060c19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Jul 2026 23:36:44 +0000 Subject: [PATCH 2/3] Fix crash when JSTypeAliasDeclaration merged with module.exports property When a @typedef and a module.exports.Property = ... assignment share the same symbol name, symbol.Declarations contains both a JSTypeAliasDeclaration and a BinaryExpression. The first-in-wins type annotation lookup in serializeTypeForDeclaration would find the JSTypeAliasDeclaration (because it has a non-nil Type()), then call GetTypeOfDeclaration on it, which panics since JSTypeAliasDeclaration is not an inferrable node. Fix: additionally filter the lookup to only include declarations for which HasInferredType is true, ensuring only inferrable nodes are passed to GetTypeOfDeclaration. Co-authored-by: RyanCavanaugh <6685088+RyanCavanaugh@users.noreply.github.com> --- internal/checker/nodebuilderimpl.go | 4 +- ...fMergedWithModuleExportProperty.errors.txt | 20 ++++++++ ...jsTypedefMergedWithModuleExportProperty.js | 48 +++++++++++++++++++ ...edefMergedWithModuleExportProperty.symbols | 30 ++++++++++++ ...ypedefMergedWithModuleExportProperty.types | 40 ++++++++++++++++ ...jsTypedefMergedWithModuleExportProperty.ts | 17 +++++++ 6 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.errors.txt create mode 100644 testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js create mode 100644 testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.symbols create mode 100644 testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.types create mode 100644 testdata/tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts diff --git a/internal/checker/nodebuilderimpl.go b/internal/checker/nodebuilderimpl.go index b1e539d621..7aca3a1316 100644 --- a/internal/checker/nodebuilderimpl.go +++ b/internal/checker/nodebuilderimpl.go @@ -2221,7 +2221,9 @@ func (b *NodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati pt = b.pc.GetTypeOfDeclaration(declaration) } if (pt == nil || pt.Kind == pseudochecker.PseudoTypeKindNoResult) && ast.IsBinaryExpression(declaration) { - if decl := core.Find(symbol.Declarations, hasTypeAnnotation); decl != nil { + if decl := core.Find(symbol.Declarations, func(d *ast.Declaration) bool { + return hasTypeAnnotation(d) && ast.HasInferredType(d) + }); decl != nil { // Binary expressions have a first-in-wins type annotation system. The first one with an annotation supplies the type for the rest. pt = b.pc.GetTypeOfDeclaration(decl) } diff --git a/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.errors.txt b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.errors.txt new file mode 100644 index 0000000000..518283e1cb --- /dev/null +++ b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.errors.txt @@ -0,0 +1,20 @@ +local-lib/ModuleGraphConnection.js(3,1): error TS2309: An export assignment cannot be used in a module with other exported elements. +local-lib/ModuleGraphConnection.js(4,16): error TS2339: Property 'T' does not exist on type 'typeof ModuleGraphConnection'. + + +==== local-lib/ModuleGraphConnection.js (2 errors) ==== + /** @typedef {typeof T} T */ + const T = Symbol(); + module.exports = class ModuleGraphConnection {}; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2309: An export assignment cannot be used in a module with other exported elements. + module.exports.T = T; + ~ +!!! error TS2339: Property 'T' does not exist on type 'typeof ModuleGraphConnection'. + +==== repro.js (0 errors) ==== + 'use strict'; + /** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ + /** @type {ImportedType} */ + module.exports = class Repro {}; + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js new file mode 100644 index 0000000000..9708ba8ee6 --- /dev/null +++ b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.js @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts] //// + +//// [ModuleGraphConnection.js] +/** @typedef {typeof T} T */ +const T = Symbol(); +module.exports = class ModuleGraphConnection {}; +module.exports.T = T; + +//// [repro.js] +'use strict'; +/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ +/** @type {ImportedType} */ +module.exports = class Repro {}; + + +//// [ModuleGraphConnection.js] +"use strict"; +/** @typedef {typeof T} T */ +const T = Symbol(); +module.exports = class ModuleGraphConnection { +}; +module.exports.T = T; +//// [repro.js] +'use strict'; +/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ +/** @type {ImportedType} */ +module.exports = class Repro { +}; + + +//// [ModuleGraphConnection.d.ts] +export = ModuleGraphConnection; +declare class ModuleGraphConnection { +} +declare namespace ModuleGraphConnection { + const _exported: typeof T; + export { _exported as T }; +} +export type T = typeof T; +/** @typedef {typeof T} T */ +declare const T: unique symbol; +//// [repro.d.ts] +export = Repro; +/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ +/** @type {ImportedType} */ +declare class Repro { +} +export type ImportedType = import('./local-lib/ModuleGraphConnection'); diff --git a/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.symbols b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.symbols new file mode 100644 index 0000000000..5cd1901280 --- /dev/null +++ b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.symbols @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts] //// + +=== local-lib/ModuleGraphConnection.js === +/** @typedef {typeof T} T */ +const T = Symbol(); +>T : Symbol(T, Decl(ModuleGraphConnection.js, 1, 5), Decl(ModuleGraphConnection.js, 0, 4)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +module.exports = class ModuleGraphConnection {}; +>module.exports : Symbol(exports, Decl(ModuleGraphConnection.js, 0, 0)) +>module : Symbol(module, Decl(ModuleGraphConnection.js, 0, 0)) +>exports : Symbol(exports, Decl(ModuleGraphConnection.js, 0, 0)) +>ModuleGraphConnection : Symbol(ModuleGraphConnection, Decl(ModuleGraphConnection.js, 2, 16)) + +module.exports.T = T; +>module.exports : Symbol(exports, Decl(ModuleGraphConnection.js, 0, 0)) +>module : Symbol(module, Decl(ModuleGraphConnection.js, 0, 0)) +>exports : Symbol(exports, Decl(ModuleGraphConnection.js, 0, 0)) +>T : Symbol(T, Decl(ModuleGraphConnection.js, 1, 5), Decl(ModuleGraphConnection.js, 0, 4)) + +=== repro.js === +'use strict'; +/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ +/** @type {ImportedType} */ +module.exports = class Repro {}; +>module.exports : Symbol(exports, Decl(repro.js, 0, 0)) +>module : Symbol(module, Decl(repro.js, 0, 0)) +>exports : Symbol(exports, Decl(repro.js, 0, 0)) +>Repro : Symbol(Repro, Decl(repro.js, 3, 16)) + diff --git a/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.types b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.types new file mode 100644 index 0000000000..eae466ce5f --- /dev/null +++ b/testdata/baselines/reference/compiler/jsTypedefMergedWithModuleExportProperty.types @@ -0,0 +1,40 @@ +//// [tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts] //// + +=== local-lib/ModuleGraphConnection.js === +/** @typedef {typeof T} T */ +const T = Symbol(); +>T : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +module.exports = class ModuleGraphConnection {}; +>module.exports = class ModuleGraphConnection {} : typeof ModuleGraphConnection +>module.exports : typeof ModuleGraphConnection +>module : { exports: typeof ModuleGraphConnection; } +>exports : typeof ModuleGraphConnection +>class ModuleGraphConnection {} : typeof ModuleGraphConnection +>ModuleGraphConnection : typeof ModuleGraphConnection + +module.exports.T = T; +>module.exports.T = T : unique symbol +>module.exports.T : any +>module.exports : typeof ModuleGraphConnection +>module : { exports: typeof ModuleGraphConnection; } +>exports : typeof ModuleGraphConnection +>T : any +>T : unique symbol + +=== repro.js === +'use strict'; +>'use strict' : "use strict" + +/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ +/** @type {ImportedType} */ +module.exports = class Repro {}; +>module.exports = class Repro {} : typeof Repro +>module.exports : typeof Repro +>module : { exports: typeof Repro; } +>exports : typeof Repro +>class Repro {} : typeof Repro +>Repro : typeof Repro + diff --git a/testdata/tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts b/testdata/tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts new file mode 100644 index 0000000000..2aa7e887f6 --- /dev/null +++ b/testdata/tests/cases/compiler/jsTypedefMergedWithModuleExportProperty.ts @@ -0,0 +1,17 @@ +// @allowJs: true +// @checkJs: true +// @target: es2015 +// @outDir: ./out +// @declaration: true + +// @filename: local-lib/ModuleGraphConnection.js +/** @typedef {typeof T} T */ +const T = Symbol(); +module.exports = class ModuleGraphConnection {}; +module.exports.T = T; + +// @filename: repro.js +'use strict'; +/** @typedef {import('./local-lib/ModuleGraphConnection')} ImportedType */ +/** @type {ImportedType} */ +module.exports = class Repro {}; From 4cf9c8fbaa16b41b9316f0f1ce9f313c52a33eb8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 21 Jul 2026 18:50:34 +0000 Subject: [PATCH 3/3] Fix hasTypeAnnotation to exclude TypeAliasDeclaration and JSTypeAliasDeclaration Per reviewer feedback, fix the issue at the root in hasTypeAnnotation rather than patching the call site. Type alias declarations have a .Type() that is their type definition, not a type annotation on a value - mirroring getEffectiveTypeAnnotationNode in TypeScript which explicitly bails on isTypeAliasDeclaration. This allows the call site in serializeTypeForDeclaration to revert to the simpler hasTypeAnnotation predicate without needing the &&ast.HasInferredType(d) guard. Co-authored-by: weswigham <2932786+weswigham@users.noreply.github.com> --- internal/checker/nodebuilderimpl.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/internal/checker/nodebuilderimpl.go b/internal/checker/nodebuilderimpl.go index 7aca3a1316..2eda08fd60 100644 --- a/internal/checker/nodebuilderimpl.go +++ b/internal/checker/nodebuilderimpl.go @@ -2160,7 +2160,15 @@ func (b *NodeBuilderImpl) indexInfoToIndexSignatureDeclarationHelper(indexInfo * } func hasTypeAnnotation(declaration *ast.Declaration) bool { - return declaration != nil && declaration.Type() != nil + if declaration == nil || declaration.Type() == nil { + return false + } + // Type alias declarations have a .Type() that is their type definition, not a type annotation on a value. + // Exclude them so callers don't mistake them for annotated value declarations. + if ast.IsTypeAliasDeclaration(declaration) || ast.IsJSTypeAliasDeclaration(declaration) { + return false + } + return true } /** @@ -2221,9 +2229,7 @@ func (b *NodeBuilderImpl) serializeTypeForDeclaration(declaration *ast.Declarati pt = b.pc.GetTypeOfDeclaration(declaration) } if (pt == nil || pt.Kind == pseudochecker.PseudoTypeKindNoResult) && ast.IsBinaryExpression(declaration) { - if decl := core.Find(symbol.Declarations, func(d *ast.Declaration) bool { - return hasTypeAnnotation(d) && ast.HasInferredType(d) - }); decl != nil { + if decl := core.Find(symbol.Declarations, hasTypeAnnotation); decl != nil { // Binary expressions have a first-in-wins type annotation system. The first one with an annotation supplies the type for the rest. pt = b.pc.GetTypeOfDeclaration(decl) }