You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
"require" condition not respected when imported package has "type": "module" #49391
My project is using "moduleResolution": "node16" in its tsconfig
My project has no "type" field in package.json, so it defaults to commonjs
Currently, Vitest doesn't have a require condition in its exports field, but even if it did, TypeScript 4.7 still assumes the package is ESM only. This leads to TypeScript thinking that vitest cannot be imported by test files, as the test files are assumed to be CJS modules, but Vitest actually compiles them as ESM.
I have a PR open (vitest-dev/vitest#1411) that removes "type": "module" and uses .mjs extensions instead, and this avoids the issue. But the Vitest team thinks this is something that TypeScript should fix. I believe this is an edge case and so Vitest should fix it, but I'm writing this issue to get the TypeScript team's opinion on the matter.
🔎 Search Terms
require condition
type module
node16 moduleResolution
nodenext
🙁 Actual behavior
When a test file imports vitest, it gets this error:
Module 'vitest' cannot be imported using this construct. The specifier only resolves to an ES module,
which cannot be imported synchronously. Use dynamic import instead.
Even if I add a require condition the exports map of Vitest.
🙂 Expected behavior
Vitest should be able to use a require condition to signal to TypeScript that CJS modules are allowed to import the vitest package without receiving an error message.
If vitest compiles its esm to cjs at runtime (build time?) and the imports will successfully resolve to cjs format files, then it's presenting a cjs api at runtime and its types should reflect that. Removing type: module and using .mjs extensions instead won't change anything - the files will still be esm format either way. If you want imports from cjs consumers to always succeed, you need to provide cjs entry points - either declaration files (or source) in a type: commonjs context or .d.cts files. I assume vitest is only used via bundler (likely vite)? Because you'd have encountered issues in node itself long ago otherwise. module: nodenext doesn't model whatever bespoke resolution vite does; it models node itself. If there's a mismatch there, you'd have to inform typescript of the equivalences via explicit type definitions.
In short, your idea of using a require condition to point TS is the kernel of the right way to go, but you also need the file you're pointing the condition at to actually be the right format - the condition alone doesn't imply the format. If you only wanna solve for TS (and not non-vite node callers), supplying a combination require + types condition pointing at a .d.cts file (or .d.ts in type: commonjs) is the way to go.
If you only wanna solve for TS (and not non-vite node callers), supplying a combination require + types condition pointing at a .d.cts file (or .d.ts in type: commonjs) is the way to go.
I can confirm your solution also removed the TypeScript warning.
It seems I was wrong. I tried to reproduce the minimal example here and I could not. It may be that I had an additional types condition pointing at the ESM and that is what had led TypeScript to consider the ESM despite the presence of require. Pardon the noise.
Activity
weswigham commentedon Jun 9, 2022
If
vitestcompiles its esm to cjs at runtime (build time?) and the imports will successfully resolve to cjs format files, then it's presenting a cjs api at runtime and its types should reflect that. Removingtype: moduleand using.mjsextensions instead won't change anything - the files will still be esm format either way. If you want imports from cjs consumers to always succeed, you need to provide cjs entry points - either declaration files (or source) in atype: commonjscontext or.d.ctsfiles. I assumevitestis only used via bundler (likelyvite)? Because you'd have encountered issues innodeitself long ago otherwise.module: nodenextdoesn't model whatever bespoke resolutionvitedoes; it models node itself. If there's a mismatch there, you'd have to inform typescript of the equivalences via explicit type definitions.weswigham commentedon Jun 9, 2022
In short, your idea of using a
requirecondition to point TS is the kernel of the right way to go, but you also need the file you're pointing the condition at to actually be the right format - the condition alone doesn't imply the format. If you only wanna solve for TS (and not non-vite node callers), supplying a combinationrequire+typescondition pointing at a.d.ctsfile (or.d.tsintype: commonjs) is the way to go.aleclarson commentedon Jun 9, 2022
Correction: It removes the TypeScript warning mentioned in the "Actual behavior" section.
aleclarson commentedon Jun 9, 2022
I can confirm your solution also removed the TypeScript warning.
I had tried only a
requirecondition before, but not a combination ofrequire/defaultandrequire/typesconditions.typescript-bot commentedon Jun 16, 2022
This issue has been marked as 'External' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
tilgovi commentedon Jul 19, 2022
tilgovi commentedon Jul 19, 2022
It seems I was wrong. I tried to reproduce the minimal example here and I could not. It may be that I had an additional
typescondition pointing at the ESM and that is what had led TypeScript to consider the ESM despite the presence ofrequire. Pardon the noise.