From 146a7bcd01113366b5168763932dfc0d43b8d175 Mon Sep 17 00:00:00 2001 From: andre-fajar-n Date: Mon, 12 Oct 2026 14:42:58 +0700 Subject: [PATCH 1/2] fix: Fix MemberList panic on enum element access with merged namespace exports --- tsc/internal/checker/checker.go | 4 ++- ...zerNamespaceExportElementAccess.errors.txt | 16 +++++++++++ ...InitializerNamespaceExportElementAccess.js | 27 +++++++++++++++++++ ...alizerNamespaceExportElementAccess.symbols | 21 +++++++++++++++ ...tializerNamespaceExportElementAccess.types | 22 +++++++++++++++ ...InitializerNamespaceExportElementAccess.ts | 9 +++++++ 6 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js create mode 100644 tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types create mode 100644 tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 345ea00ce1437..370f05963e5dd 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -24600,7 +24600,9 @@ func (c *Checker) evaluateEntity(expr *ast.Node, location *ast.Node) evaluator.R if rootSymbol != nil && rootSymbol.Flags()&ast.SymbolFlagsEnum != 0 { name := expr.AsElementAccessExpression().ArgumentExpression.Text() member := rootSymbol.Exports()[name] - if member != nil { + // Merged namespace+enum symbols can export non-enum members (e.g. a class). + // Only treat EnumMember exports as constant enum values — mirror the identifier path. + if member != nil && member.Flags()&ast.SymbolFlagsEnumMember != 0 { if location != nil { return c.evaluateEnumMember(expr, member, location) } diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt new file mode 100644 index 0000000000000..69bf95c4d7248 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt @@ -0,0 +1,16 @@ +enumInitializerNamespaceExportElementAccess.ts(8,9): error TS18033: Type 'typeof C' is not assignable to type 'number' as required for computed enum member values. + + +==== enumInitializerNamespaceExportElementAccess.ts (1 errors) ==== + // Namespace export merged with enum must not be treated as an enum member via E["C"]. + // Previously panicked: Unhandled case in Node.MemberList: KindModuleBlock + + namespace E { + export class C {} + } + enum E { + D = E["C"], + ~~~~~~ +!!! error TS18033: Type 'typeof C' is not assignable to type 'number' as required for computed enum member values. + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js new file mode 100644 index 0000000000000..559adf2a32a78 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts] //// + +//// [enumInitializerNamespaceExportElementAccess.ts] +// Namespace export merged with enum must not be treated as an enum member via E["C"]. +// Previously panicked: Unhandled case in Node.MemberList: KindModuleBlock + +namespace E { + export class C {} +} +enum E { + D = E["C"], +} + + +//// [enumInitializerNamespaceExportElementAccess.js] +"use strict"; +// Namespace export merged with enum must not be treated as an enum member via E["C"]. +// Previously panicked: Unhandled case in Node.MemberList: KindModuleBlock +var E; +(function (E) { + class C { + } + E.C = C; +})(E || (E = {})); +(function (E) { + E[E["D"] = E["C"]] = "D"; +})(E || (E = {})); diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols new file mode 100644 index 0000000000000..63f06800aca92 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts] //// + +=== enumInitializerNamespaceExportElementAccess.ts === +// Namespace export merged with enum must not be treated as an enum member via E["C"]. +// Previously panicked: Unhandled case in Node.MemberList: KindModuleBlock + +namespace E { +>E : Symbol(E, Decl(enumInitializerNamespaceExportElementAccess.ts, 0, 0), Decl(enumInitializerNamespaceExportElementAccess.ts, 5, 1)) + + export class C {} +>C : Symbol(C, Decl(enumInitializerNamespaceExportElementAccess.ts, 3, 13)) +} +enum E { +>E : Symbol(E, Decl(enumInitializerNamespaceExportElementAccess.ts, 0, 0), Decl(enumInitializerNamespaceExportElementAccess.ts, 5, 1)) + + D = E["C"], +>D : Symbol(E.D, Decl(enumInitializerNamespaceExportElementAccess.ts, 6, 8)) +>E : Symbol(E, Decl(enumInitializerNamespaceExportElementAccess.ts, 0, 0), Decl(enumInitializerNamespaceExportElementAccess.ts, 5, 1)) +>"C" : Symbol(E.C, Decl(enumInitializerNamespaceExportElementAccess.ts, 3, 13)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types new file mode 100644 index 0000000000000..9cd29af7731e9 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts] //// + +=== enumInitializerNamespaceExportElementAccess.ts === +// Namespace export merged with enum must not be treated as an enum member via E["C"]. +// Previously panicked: Unhandled case in Node.MemberList: KindModuleBlock + +namespace E { +>E : typeof E + + export class C {} +>C : C +} +enum E { +>E : E + + D = E["C"], +>D : E.D +>E["C"] : typeof E.C +>E : typeof E +>"C" : "C" +} + diff --git a/tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts b/tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts new file mode 100644 index 0000000000000..e36ee8c24e47f --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts @@ -0,0 +1,9 @@ +// Namespace export merged with enum must not be treated as an enum member via E["C"]. +// Previously panicked: Unhandled case in Node.MemberList: KindModuleBlock + +namespace E { + export class C {} +} +enum E { + D = E["C"], +} From c22763173b575b8575f01e216d7fff744c48cf73 Mon Sep 17 00:00:00 2001 From: andre-fajar-n Date: Mon, 12 Oct 2026 15:20:42 +0700 Subject: [PATCH 2/2] fix: Resolve aliases before EnumMember check in enum element access --- tsc/internal/checker/checker.go | 18 ++++++++---- ...zerNamespaceExportElementAccess.errors.txt | 12 ++++++++ ...InitializerNamespaceExportElementAccess.js | 25 ++++++++++++++++ ...alizerNamespaceExportElementAccess.symbols | 27 +++++++++++++++++ ...tializerNamespaceExportElementAccess.types | 29 +++++++++++++++++++ ...InitializerNamespaceExportElementAccess.ts | 12 ++++++++ 6 files changed, 117 insertions(+), 6 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 370f05963e5dd..6e57ed8d7deeb 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -24600,13 +24600,19 @@ func (c *Checker) evaluateEntity(expr *ast.Node, location *ast.Node) evaluator.R if rootSymbol != nil && rootSymbol.Flags()&ast.SymbolFlagsEnum != 0 { name := expr.AsElementAccessExpression().ArgumentExpression.Text() member := rootSymbol.Exports()[name] - // Merged namespace+enum symbols can export non-enum members (e.g. a class). - // Only treat EnumMember exports as constant enum values — mirror the identifier path. - if member != nil && member.Flags()&ast.SymbolFlagsEnumMember != 0 { - if location != nil { - return c.evaluateEnumMember(expr, member, location) + if member != nil { + // Follow aliases like resolveEntityName (property-access path), e.g. export import C = F.A. + for member.Flags()&ast.SymbolFlagsValue == 0 && member.Flags()&ast.SymbolFlagsAlias != 0 { + member = c.resolveAlias(member) + } + // Merged namespace+enum symbols can export non-enum members (e.g. a class). + // Only treat EnumMember exports as constant enum values — mirror the identifier path. + if member.Flags()&ast.SymbolFlagsEnumMember != 0 { + if location != nil { + return c.evaluateEnumMember(expr, member, location) + } + return c.getEnumMemberValue(member.ValueDeclaration()) } - return c.getEnumMemberValue(member.ValueDeclaration()) } } } diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt index 69bf95c4d7248..7818ffce72b99 100644 --- a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.errors.txt @@ -13,4 +13,16 @@ enumInitializerNamespaceExportElementAccess.ts(8,9): error TS18033: Type 'typeof ~~~~~~ !!! error TS18033: Type 'typeof C' is not assignable to type 'number' as required for computed enum member values. } + + // Alias exports that resolve to enum members must still constant-evaluate (like E.C). + enum F { + A = 1, + } + namespace E2 { + export import C = F.A; + } + enum E2 { + D = E2["C"], + Next, + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js index 559adf2a32a78..e5312dbec4852 100644 --- a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.js @@ -10,6 +10,18 @@ namespace E { enum E { D = E["C"], } + +// Alias exports that resolve to enum members must still constant-evaluate (like E.C). +enum F { + A = 1, +} +namespace E2 { + export import C = F.A; +} +enum E2 { + D = E2["C"], + Next, +} //// [enumInitializerNamespaceExportElementAccess.js] @@ -25,3 +37,16 @@ var E; (function (E) { E[E["D"] = E["C"]] = "D"; })(E || (E = {})); +// Alias exports that resolve to enum members must still constant-evaluate (like E.C). +var F; +(function (F) { + F[F["A"] = 1] = "A"; +})(F || (F = {})); +var E2; +(function (E2) { + E2.C = F.A; +})(E2 || (E2 = {})); +(function (E2) { + E2[E2["D"] = 1] = "D"; + E2[E2["Next"] = 2] = "Next"; +})(E2 || (E2 = {})); diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols index 63f06800aca92..f5e6db5dfc5fe 100644 --- a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.symbols @@ -19,3 +19,30 @@ enum E { >"C" : Symbol(E.C, Decl(enumInitializerNamespaceExportElementAccess.ts, 3, 13)) } +// Alias exports that resolve to enum members must still constant-evaluate (like E.C). +enum F { +>F : Symbol(F, Decl(enumInitializerNamespaceExportElementAccess.ts, 8, 1)) + + A = 1, +>A : Symbol(F.A, Decl(enumInitializerNamespaceExportElementAccess.ts, 11, 8)) +} +namespace E2 { +>E2 : Symbol(E2, Decl(enumInitializerNamespaceExportElementAccess.ts, 13, 1), Decl(enumInitializerNamespaceExportElementAccess.ts, 16, 1)) + + export import C = F.A; +>C : Symbol(C, Decl(enumInitializerNamespaceExportElementAccess.ts, 14, 14)) +>F : Symbol(F, Decl(enumInitializerNamespaceExportElementAccess.ts, 8, 1)) +>A : Symbol(C, Decl(enumInitializerNamespaceExportElementAccess.ts, 11, 8)) +} +enum E2 { +>E2 : Symbol(E2, Decl(enumInitializerNamespaceExportElementAccess.ts, 13, 1), Decl(enumInitializerNamespaceExportElementAccess.ts, 16, 1)) + + D = E2["C"], +>D : Symbol(E2.D, Decl(enumInitializerNamespaceExportElementAccess.ts, 17, 9)) +>E2 : Symbol(E2, Decl(enumInitializerNamespaceExportElementAccess.ts, 13, 1), Decl(enumInitializerNamespaceExportElementAccess.ts, 16, 1)) +>"C" : Symbol(E2.C, Decl(enumInitializerNamespaceExportElementAccess.ts, 14, 14)) + + Next, +>Next : Symbol(E2.Next, Decl(enumInitializerNamespaceExportElementAccess.ts, 18, 16)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types index 9cd29af7731e9..ade9d221407d6 100644 --- a/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types +++ b/tsc/testdata/baselines/reference/compiler/enumInitializerNamespaceExportElementAccess.types @@ -20,3 +20,32 @@ enum E { >"C" : "C" } +// Alias exports that resolve to enum members must still constant-evaluate (like E.C). +enum F { +>F : F + + A = 1, +>A : F.A +>1 : 1 +} +namespace E2 { +>E2 : typeof E2 + + export import C = F.A; +>C : F.A +>F : F +>A : F.A +} +enum E2 { +>E2 : E2 + + D = E2["C"], +>D : E2.D +>E2["C"] : F +>E2 : typeof E2 +>"C" : "C" + + Next, +>Next : E2.Next +} + diff --git a/tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts b/tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts index e36ee8c24e47f..9a40c89e1baf4 100644 --- a/tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts +++ b/tsc/testdata/tests/cases/compiler/enumInitializerNamespaceExportElementAccess.ts @@ -7,3 +7,15 @@ namespace E { enum E { D = E["C"], } + +// Alias exports that resolve to enum members must still constant-evaluate (like E.C). +enum F { + A = 1, +} +namespace E2 { + export import C = F.A; +} +enum E2 { + D = E2["C"], + Next, +}