-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Should type-only import resolving to ES module from CJS module be allowed? #52529
Comments
Yes, please. Is it hard to suppress the error when doing |
No, it’s not hard, we just have to be sure it makes sense and doesn’t have any other unintended consequences. |
One potential risk is that nominal types could accidentally leak into the project in two versions - one coming from the CJS types and one from the module types. This wouldn't exactly be a new risk but this could make it more common. I'm not saying that this should be a blocker - being able to use types from modules in CJS type definitions would solve some of my problems. It is an important consideration though. |
Isn’t the only truly nominal type in TS |
Arent classes with private members nominal? |
Oh, good point; yes, they are. Not sure how I forgot about that! |
Even if one follows the instructions in the error message and tries to use dynamic-import-type syntax, the error persists: type Terser = typeof import('terser');
// The current file is a CommonJS module whose imports will produce
// 'require' calls; however, the referenced file is an ECMAScript module
// and cannot be imported with 'require'. Consider writing a dynamic
// 'import("terser")' call instead. The only workaround I've found is to write a real function that does the dynamic import and infer the types off of that: async function loadTerser() {
return await import('terser');
}
type Terser = Awaited<ReturnType<typeof loadTerser>>; |
The instructions are telling you to write the latter because the error message doesn’t realize you were writing a type-only import to begin with. But something is still weird with that error being issued on an ImportType (the former) 🤔. |
For the record, in CommonJS files, even if you write a dynamic import, it will still be transformed to a |
That doesn't sound correct, perhaps it's sensitive to other compiler options related to target and stuff but with this input: // @module: commonjs
// @moduleResolution: node16
export const bar = 1
import('foo').then(a => {}) I get this CJS output: "use strict";
// @module: commonjs
// @moduleResolution: node16
Object.defineProperty(exports, "__esModule", { value: true });
exports.bar = void 0;
exports.bar = 1;
import('foo').then(a => { }); |
Ah, it works with moduleResolution |
Hold up, the module resolution mode affects the emit here? |
@andrewbranch Seems to - changing the moduleResolution to |
There are several issues that exist around this - I think the gist that I haven't seen written anywhere is this: dynamic imports of ESM code into a CJS module as supported by Node is essentially crippled in TypeScript at the moment, because you can't use types anywhere. The original This will be more and more of an issue as developers only release ESM packages and not all codebases are in a place to switch 100% to ESM. |
Thank you @andrewbranch - I was just thinking about one clarifying point. Ideally, a CommonJS package that dynamically imports an ESM module and consumes the ESM modules' types should be able to be used in a CJS project without the CJS project caring at all that the dependency happens to dynamic-import an ESM. When testing this scenario myself, I had compilation errors in my CJS project if I didn't change "module" to node16. The CJS project shouldn't have to care about the dependency doing a dynamic import/importing types from an ESM, right? |
Kind of yes, kind of no? Ideally, there are two kinds of things in declaration files:
When you import any dependency, you have reason to care about everything in category (1), because your program options define what runtime constructs are available and what semantics they have. So, to take your example, if you import a CJS dependency, and the declaration file for that dependency indicates that there is a real (category 1) dynamic import of an ESM file, your program options better indicate that your runtime can handle a CommonJS file that contains a dynamic import of an ESM file, and your options likely also need to indicate what the semantics of that dynamic import are—e.g., how the import path is resolved to a file—so that types for the dynamically imported module can be properly acquired. Things in category (2), like type-only imports, don’t have this property of needing to be scrutinized for runtime compatibility. They simply need to be coherent and unambiguous so your program can find the types that the author intended to reference. This is the correct mental model to have in the abstract. In your specific example, I can’t think of a real, existing configuration that should have a problem. But the semantics of imports in your dependencies’ declaration files are very much affected by your own program settings, because imports in your dependencies’ implementation files are affected by your own runtime environment.
I don’t know what’s going on without seeing the errors, but |
Thank you for this response! That makes perfect sense. This is exactly what I needed to hear:
|
#53656 is the solution to this problem, which is on the 5.3 iteration plan. |
And yeah, aside from that, the error message needs to be overhauled. If they don’t want to break backward compatibility with old TS versions, they can use an import type ( |
@andrewbranch I still got a question. The rationale behind import attributes is security. If I use explicit |
Security isn’t an issue here, but there are other reasons not to do it. I tried to relax the rules; the objections were discussed in the PR #53426 |
Is there a solution/workaround for this in the meantime? |
@hahnbeelee basically |
This works, thank you!
5.3 has now been released. What's the next step toward a more permanent solution for this issue? |
Bug Report
🕗 Version & Regression Information
I observed this in TypeScript 4.9.x, couldn't test earlier versions.
💻 Code
tsconfig.json:
package.json:
index.ts:
🙁 Actual behavior
Error reported:
Note the got is just a quick repro example. It happening with any ESM module.
🙂 Expected behavior
Since I'm just doing
import type
, the emitted file has no requires at all and thus the error is unnecessary.The text was updated successfully, but these errors were encountered: