Add verbatimModuleSyntax
, deprecate importsNotUsedAsValues
and preserveValueImports
by andrewbranch · Pull Request #52203 · microsoft/TypeScript (original) (raw)
Closes #51479
This implements pretty much exactly what I proposed in #51479 so I won’t fully re-explain it here. Instead, I’ll call attention to a couple things I didn’t discuss in the proposal.
CommonJS module exports are severely limited
CommonJS JavaScript modules use require
and module.exports =
for importing/exporting code. TypeScript has direct equivalents for the basic forms of these:
// JS const fs = require("fs"); module.exports = {}; // TS import fs = require("fs"); export = {};
However, TypeScript lacks direct equivalents for destructuring a require
or doing property assignment exports:
// JS const { writeFile } = require("fs"); module.exports.hello = "world";
It is common to approximate this behavior with ESM syntax in TypeScript:
import { writeFile } from "fs"; export const hello = "world";
But this is, in fact, ESM syntax trying to represent a CommonJS module, and is not allowed under verbatimModuleSyntax
. The direct translation is:
import fs = require("fs");
// must use fs.writeFile
or assign to a variable
export = { hello: "world" };
We knew this would be a bit of a burden for imports, but I didn’t realize what a pain it would be for exports. The problem here is that you can no longer sidecar-export a type:
export type T = {}; export = { hello: "world" }; // ^^^^^^^^^^^^^^^^^^^^^^^^ // An export assignment cannot be used in a module with other exported elements.(2309)
To accomplish this, you’d have to merge a namespace declaration containing the types:
const _exports = { hello: "world" }; namespace _exports { export type T = {}; } export = _exports;
These limitations make it really unattractive to use the flag in CJS-emitting TypeScript. I discussed this with @DanielRosenwasser, and ultimately decided that it’s not worth worrying about too much—the flag is opt-in, and we expect the users interested in using it to write mostly ESM-emitting TypeScript.
Improved auto-imports for CommonJS imports
Despite the above, I did spend a couple minutes to make auto-imports in CJS files work a bit better. Since import { writeFile } from "fs"
is not allowed in CJS files, when you start typing writeFile
, you now get an auto import of fs
and the qualification on writeFile
at the same time:
🎥 GIF
Pending codefix
I ensured auto-imports work well with the new mode, but I haven’t yet added any codefixes or reviewed the existing ones—I think there are existing ones to add type-only annotations to imports as necessary, but I’ll likely need to add some for transforming imports between CJS and ESM syntaxes. I wanted to get this PR up so it has time to be reviewed before the beta, but I plan to add codefixes so hopefully codebases can be transitioned onto the flag with ts-fix.