Description
Background
Today, TypeScript has two uses of the module
keyword.
The first is called an "ambient module declaration".
declare module "some-library" {
// ...
}
declare module "*.css";
While we often prefer people author declaration files and submit them to DefinitelyTyped, the declare module
syntax here is fine.
The second use is for namespace declarations.
module foo {}
declare module bar.baz {}
This is odd - these are called "namespace" declarations? Well, namespace declarations support using either the namespace
keyword, or the module
keyword. You could replace module
with namespace
in 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 namespace
keyword to make the distinction clearer. Over time, the community preferred using the namespace
keyword and linters discouraged the use of module
in 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 module
for 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 module
keyword 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 with namespace
. 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 module
keyword-declared namespaces in .d.ts
files 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 contain module
keyword-declared namespaces.
Reconsideration
Here's a few points to consider:
- 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. - Forcing namespace users off of
module
in implementation files seems fine, but it feels kind of pointless if we can't break them in declaration files. - 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.