Repository navigation
Consider deprecating namespaces declared with the module keyword #51825
Description
Activity
- addedSuggestionAn idea for TypeScriptAn idea for TypeScriptIn DiscussionNot yet reached consensusNot yet reached consensus
on Dec 8, 2022 To some extent, erroring on a keyword that no new code uses anyway feels like a break for the sake of purity. It heavily favors conceptual cleanliness for implementers without much benefit for people using TypeScript.
If
moduleas an alias fornamespaceisn’t deprecated, won’t it eventually clash with the mentioned ES proposal(s)? So it seems like maintaining the status quo simply isn’t an option in the long term, and the points made under “Reconsideration” are therefore moot. See: the turmoil caused byexperimentalDecorators(which is flagged!)Thank you for raising this!
This looks like an excellent deprecation opportunity that will reduce the current confusion caused by having two ways to declare namespaces. Especially when it's clear cut that one keyword is preferred (
namespace) and the other keyword is legacy (module). It's great that TypeScript can evolve and steer users towards intended usage.Experience in Bloomberg
Here is some context on general namespace usage within the Bloomberg JS environment. This runtime approximates a pure ES modules-based environment which means no need to cater to CommonJS.
Linting in Bloomberg’s environment ensures that namespaces are only permitted in declaration files and can only use the modern
declare namespacesyntax. Meaning namespaces are not used in any files that generate JavaScript. This ensures that non-JS syntax is only used for static type annotations that disappear at build-time.When one imposes linting restrictions on a developer community, it usually results in questions like "why am I not allowed to do that?" The internal feedback on these restrictions we've heard is:
- Zero users have asked for the legacy
modulesyntax for namespaces (in either declarations or*.tsfiles) - One or two users have asked to use namespaces in
*.tsfiles- ...and after explaining to them that it wasn't essential or recommended, they dropped the request
- Relatively speaking, probably 20x more people ask why
enumis unavailable in*.tsfiles compared to why namespaces are not available
- Usage of modern
declare namespacesyntax in declaration files is used a small amount (3% of projects)- Lots of these are where folk are pairing a hand-authored declaration file alongside a yet-to-be-converted-to-TypeScript
*.jsfile. A quick inspection suggests these could all be better if they were migrated to regular interfaces. - Many of these namespace usages are for vendored code from npm/DefinitelyTyped where the folk doing the vendoring have tried to avoid forking/modifying the original declaration files.
- Lots of these are where folk are pairing a hand-authored declaration file alongside a yet-to-be-converted-to-TypeScript
The General Case for Removal
(Let's ignore the specifics of what
modulemight be used for in future)The main argument I would make here is that it's positive for the TypeScript ecosystem as a whole to minimize confusing relics. And it's positive for the JavaScript ecosystem to gain access to prime ergonomic syntax.
Given the modern-day lack of enthusiasm for any kind of namespace usage,
module-keyword namespaces are a niche of a niche. Whilst they may have a meaningful count on public code searches, they are not widely understood. Even experienced TypeScript developers struggle to recall and explain the difference between ambient module declarations andmodule-keyword namespaces. Fixing this confusion via a clear and decisive deprecate-then-remove story is a net win that justifies the migration cost in my opinion.With any migration, it's all made easier by starting earlier, which means starting as soon as possible on the implementation files. With the current deprecation proposal, that will be around three years until full removal from implementation files. That sets the scene for the subsequent removal from declaration files once usage is low enough.
Next Step Suggestions
Auto-Modernizing Declaration Emit
Today compiling the TypeScript file
declare module foo {}results in the DTS filedeclare module foo {}rather than the preferred DTS filedeclare namespace foo {}To accelerate migration within the declaration file ecosystem, maybe we could immediately update TypeScript's declaration emitter to output modern syntax even if the implementation file continues to use the legacy syntax.
DefinitelyTyped Migrations
This could be a big topic all by itself. Given that there is some level of ecosystem centralization around DefinitelyTyped, can we seed trends there to assist declaration syntax migrations?
- Can we enhance the DefinitelyTyped linting to warn/reject on deprecated syntax in declaration files?
- Can we automatically generate PRs to the source repos that contribute to DefinitelyTyped?
- Andrew Branch (@andrewbranch) suggested this might be the way to go.
I'd like to hear more from DT experts on this.
Telemetry
Another big topic. VS Code already implements opt-out remote reporting of TypeScript language server crash stacks today. The documentation says it also passes on some usage data. Maybe this could be expanded, in a very minimal anonymized way, to convey when usage of a legacy TypeScript feature has been detected. This would help provide confidence that the final removal from declaration files could proceed without impacting too many users.
Summary
Deprecating and removing the legacy keyword usage seems like an excellent move and is highly appreciated. Whilst the keyword could be left around forever as a curiosity, taking proactive steps towards improving both the TypeScript and JavaScript languages helps all users and is the kind of healthy stewardship TypeScript is known for.
Reacted by Kubilay Kahveci, Daniel Ehrenberg, Thomas Chetwin and ExE Boss- Zero users have asked for the legacy
Chi Leung (@CC972) is about to start work on Auto-Modernizing Declaration Emit as described above supported by Titian Cernicova-Dragomir (@dragomirtitian).
Reacted by Titian Cernicova-Dragomir and ExE BossJoshuaKGoldberg commented
on Jul 18, 2023 ContributorMore actionsDefinitelyTyped Migrations
I'm not a DT expert but have been working with DT folks recently on DefinitelyTyped/DefinitelyTyped#65993. The points you've suggested all sound very doable to me.
Can we enhance the DefinitelyTyped linting to warn/reject on deprecated syntax in declaration files?
Yes: https://typescript-eslint.io/rules/prefer-namespace-keyword. The repo already has support for typescript-eslint rules. We can enable that rule in the repo and use the rule's auto-fixer.
Can we automatically generate PRs to the source repos that contribute to DefinitelyTyped?
Yes, and I'd be happy to help with that. One can imagine a script like:
for repo in a b c; do gh repo clone a cd a npx eslint . --config /path/to/just/prefer-namespace-keyword/config --fix # git commands to create branch and send PR cd .. end
Reacted by Rob Palmer and Alexey BerezinMy goodness. Josh Ghoulberg 👻 (@JoshuaKGoldberg) I reviewed your linked DT issue and you are not messing around.
This is an excellent plan. Thank you for volunteering.
If you need anything, please shout and I will arrange assistance.
Reacted by Josh Ghoulberg 👻 and Alexey BerezinJoshuaKGoldberg commented
on Aug 5, 2023 ContributorMore actionsI started on DefinitelyTyped/DefinitelyTyped#66292. It was actually pretty quick. If you want me to apply the same fixing to any other repo, I can be shouted at here/anywhere and/or tagged an issue. (Rob Palmer (@robpalme) I do love the idea of giving you an excuse to tell me to go do work 😄)
The work to deprecate is higher than the maintenance of the module keyword over the last 8 years. In fact, writing this deprecation plan was more work than that.
While the maintenance cost may be near-zero, the education cost is likely higher. I've found that I've had to repeatedly explain to learners why searches for "typescript modules" & similar terms end up with this old
modulekeyword. It's a mild pain that sometimes trips people up. +1 to removing on those grounds.Reacted by Titian Cernicova-DragomirYou are a good egg, Josh. Thanks for pushing this forwards.
Reacted by Josh Ghoulberg 👻As a refresher on the current status:
- Auto-upgrading of
moduletonamespacein DTS emit landed in TS 5.2 thanks to Chi Leung (@CC972) 👍 - DefinitelyTyped was converted to use
namespacein August 2023, including a lint rule to prevent new occurrences ofmodule, thanks to Josh Ghoulberg 👻 (@JoshuaKGoldberg) 👍
Reacted by Ryan Cavanaugh, Titian Cernicova-Dragomir, Josh Ghoulberg 👻 and ExE Boss- Auto-upgrading of
After speaking with Ryan Cavanaugh (@RyanCavanaugh) it seems like there may still be some concern about impacting active projects that want to upgrade TypeScript but also consume legacy DTS files. For example, where they vendored/snapshotted DTS files containing the old keyword from an unmaintained project.
So what can we do now to keep things moving forwards?
JSDoc offers the
/**@deprecated*/tag that does not error, but provides visual feedback of the deprecation along with potential for a quickfix.How about we do the same for the
modulekeyword? This would only be at the declaration site and only on the keyword, not the identifier.A built-in quickfix would be offered to convert this to
namespace. This would provide a useful nudge forwards to the modern keyword without breaking compatibility.Reacted by Holger Jeromin, Josh Ghoulberg 👻, Ryan Cavanaugh and ExE BossDanielRosenwasser commented
on Mar 22, 2024 MemberAuthorMore actionsI think we'd be okay with something that issues a language service deprecation diagnostic. I don't know if we really need the quick fix, but not opposed either.
Reacted by Ryan Cavanaugh, Rob Palmer and ExE BossRyanCavanaugh commented
on Mar 22, 2024 MemberMore actionsReacted by ExE Boss- added a commit that references this issue
on Feb 12, 2025 - added a commit that references this issue
on Nov 25, 2025


Background
Today, TypeScript has two uses of the
modulekeyword.The first is called an "ambient module declaration".
While we often prefer people author declaration files and submit them to DefinitelyTyped, the
declare modulesyntax here is fine.The second use is for namespace declarations.
This is odd - these are called "namespace" declarations? Well, namespace declarations support using either the
namespacekeyword, or themodulekeyword. You could replacemodulewithnamespacein that last code snippet and it would work the same.But why? Why support two ways to do the same thing? Because originally, TypeScript was designed with speculation that JavaScript would have two organizational schemes: "external modules" and "internal modules". External modules were files with isolated top-level scopes that could import/export values. Internal modules were named blocks of code that could export values.
Eventually, JavaScript just got "external modules" and called them "modules". TypeScript was left with two concepts and eventually it was too confusing. So in TypeScript 1.5, we introduced the
namespacekeyword to make the distinction clearer. Over time, the community preferred using thenamespacekeyword and linters discouraged the use ofmodulein their place.Recently, TC39 has been discussing two additions to JavaScript - module expressions and module declarations. These proposals have some overlap with namespaces, but ultimately act different and enable different functionality. Regardless of whether we support the proposals or they go in, during discussion, most people were surprised that the use of
modulefor namespaces wasn't at least deprecated.As we discuss TypeScript 5.0, we're considering some cleanup in the flag and language space.
Deprecation Proposal
In #51813, we discussed the matter and felt comfortable with deprecating the use of the
modulekeyword in namespace declarations. Deprecation plans are guided by plans discussed at #51000.What this would amount to is a graceful parse on any namespace declaration declared with
module. In editor scenarios, our language service would provide a quick fix to replace that keyword withnamespace. When"ignoreDeprecations"": "5.0"is turned on, we would still provide an editor suggestion which would enable the same quick fix.Caveats
This plan seems sound in implementation files; however, if a
module-declared namespace appears in a declaration file from a 3rd party package, you're out of luck. At best, we could only error on implementation files.So it's unclear if we'll be stuck with
modulekeyword-declared namespaces in.d.tsfiles forever, or if this is possibly the first step in phasing that out too. Even if we wanted to support the module declarations proposal, it seems unclear whether we could when declaration files still containmodulekeyword-declared namespaces.Reconsideration
Here's a few points to consider:
modulekeyword over the last 8 years. In fact, writing this deprecation plan was more work than that.modulein implementation files seems fine, but it feels kind of pointless if we can't break them in declaration files.