Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support type-only/uninstantiated namespaces #32

Merged
merged 4 commits into from
Jan 29, 2025
Merged

Conversation

acutmore
Copy link
Collaborator

@acutmore acutmore commented Jan 27, 2025

This PR adds support for type-only/uninstantiated namespaces. i.e. namespaces that only use type and interface.

export class C {}

export namespace C {
  export type T = string;
}

Before: Error. only declare namespace ... was allowed ❌

Now: Ok. namespace declaration erased ✅


The following cases remain unchanged:

// Instantiated namespace errors
namespace A { export let x = 1 }                // error
namespace B { ; }                               // error

// Ambient namespace is erased
declare namespace C { export let x = 1 }        // ok erased
declare module    D { export let x = 1 }        // ok erased

// `module`-keyword errors
module E { export let x = 1 }                   // error
module F { export type x = number }             // error

Testing

Unit tests for both supported cases and unsupported cases (errors) have been added.

Context

This addition was motivated by --erasableSyntaxOnly and the recognition that in today's TypeScript, the only way to augment a class with types after the initial declaration is via namespaces. Whilst that use case can be solved using declare namespace that comes with a hazard. The combination of --erasableSyntaxOnly checks and transforming uninstantiated namespaces provides a safer option.

Thanks to @jakebailey for bringing this to our attention microsoft/TypeScript#61011 (comment)

Signed-off-by: Ashley Claymore <[email protected]>
@acutmore acutmore added the enhancement New feature or request label Jan 27, 2025
Signed-off-by: Ashley Claymore <[email protected]>
Signed-off-by: Ashley Claymore <[email protected]>
mkubilayk
mkubilayk previously approved these changes Jan 28, 2025
robpalme
robpalme previously approved these changes Jan 28, 2025
docs/unsupported_syntax.md Outdated Show resolved Hide resolved
docs/unsupported_syntax.md Outdated Show resolved Hide resolved
docs/unsupported_syntax.md Outdated Show resolved Hide resolved
docs/unsupported_syntax.md Outdated Show resolved Hide resolved
docs/unsupported_syntax.md Outdated Show resolved Hide resolved
docs/unsupported_syntax.md Outdated Show resolved Hide resolved
Co-authored-by: Rob Palmer <[email protected]>
Signed-off-by: Ashley Claymore <[email protected]>
@acutmore acutmore dismissed stale reviews from robpalme and mkubilayk via 744aca8 January 28, 2025 19:55
@acutmore acutmore merged commit 4eeb96b into main Jan 29, 2025
2 checks passed
@acutmore acutmore deleted the namespaces branch January 29, 2025 10:30
}

namespace With.Imports {
import Types = My.Internal.Types;
Copy link

@magic-akari magic-akari Feb 1, 2025

Choose a reason for hiding this comment

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

Our one-pass design (SWC), while optimal for speed, inherently defers complex symbol resolution — a trade-off that aligns with tsc's isolatedModules for explicit import type syntax in ESM interactions.

However, this architecture faces new challenges as we now support namespaces. Unlike ESM's enforced type/value distinction, namespace imports/exports (e.g., My.Internal.Types) require non-trivial static analysis to resolve ambiguities:

  • Prior Approach: Namespaces were ignored, sidestepping the need for deep resolution.
  • Current Need: Determining if Types is a value or type within namespaces demands preliminary metadata collection, contradicting our single-scan constraint.

Adopting TypeScript syntax that mirrors ESM's explicitness — such as:

import type Types = My.Internal.Types;  // namespace type binding

would extend the same design principle to namespaces. By declaring intent upfront, the compiler could resolve symbols in a single pass without heuristic fallbacks, bridging the gap between ESM rigor and namespace flexibility.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Opened #38 to add more test cases.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants