diff --git a/Execution/Interpreter.Namespaces.cs b/Execution/Interpreter.Namespaces.cs
index e9bab8e2..462f8eb9 100644
--- a/Execution/Interpreter.Namespaces.cs
+++ b/Execution/Interpreter.Namespaces.cs
@@ -16,8 +16,11 @@ private ExecutionResult ExecuteNamespace(Stmt.Namespace ns)
{
string name = ns.Name.Lexeme;
- // Get or create namespace object
- SharpTSNamespace? existingNs = _environment.GetNamespace(name);
+ // Get or create namespace object — check ONLY the current scope, not up the chain.
+ // GetNamespace walks up and would find a same-named outer namespace (e.g. top-level A
+ // when declaring O.A), incorrectly merging the nested namespace into the outer one and
+ // leaving the current scope without an "A" binding (#746).
+ SharpTSNamespace? existingNs = _environment.GetLocalNamespace(name);
SharpTSNamespace nsObj;
if (existingNs != null)
diff --git a/Runtime/RuntimeEnvironment.cs b/Runtime/RuntimeEnvironment.cs
index 5d71e3a0..3647be75 100644
--- a/Runtime/RuntimeEnvironment.cs
+++ b/Runtime/RuntimeEnvironment.cs
@@ -154,6 +154,17 @@ public void DefineNamespace(string name, SharpTSNamespace ns)
return Enclosing?.GetNamespace(name);
}
+ ///
+ /// Gets a namespace by name from THIS scope only (no chain traversal).
+ /// Use when deciding whether to merge vs. create a new namespace declaration —
+ /// avoids treating a same-named namespace in an enclosing scope as a merge target (#746).
+ ///
+ public SharpTSNamespace? GetLocalNamespace(string name)
+ {
+ _namespaces.TryGetValue(name, out var ns);
+ return ns;
+ }
+
///
/// Defines a variable with a boxed value (legacy compatibility).
/// Wraps the value in RuntimeValue.FromBoxed automatically.
diff --git a/SharpTS.Tests/SharedTests/NamespaceTests.cs b/SharpTS.Tests/SharedTests/NamespaceTests.cs
index 42fae33a..1a23b77f 100644
--- a/SharpTS.Tests/SharedTests/NamespaceTests.cs
+++ b/SharpTS.Tests/SharedTests/NamespaceTests.cs
@@ -796,5 +796,25 @@ export namespace I {
Assert.Equal("2\n", TestHarness.Run(code, mode));
}
+ [Theory]
+ [MemberData(nameof(ExecutionModes.All), MemberType = typeof(ExecutionModes))]
+ public void NestedNamespace_ShadowsSameNamedTopLevelNamespace(ExecutionMode mode)
+ {
+ // Nested O.A shadows the top-level A for bare references from sibling O.B.
+ // The interpreter previously merged O.A's members into the global A and left
+ // namespaceEnvO without an "A" binding, so the pre-resolved distance-2 lookup
+ // returned Undefined → "Only instances and objects have properties" (#746).
+ var code = @"
+ namespace A { export function g() { return 100; } }
+ namespace O {
+ export namespace A { export function g() { return 5; } }
+ export namespace B { export function f() { return A.g(); } }
+ }
+ console.log(O.B.f());
+ console.log(A.g());
+ ";
+ Assert.Equal("5\n100\n", TestHarness.Run(code, mode));
+ }
+
#endregion
}