Repository navigation
"moduleResolution": "node12" of Typescript 4.5 does not work as expected #46408
Description
Activity
MartinJohns commented
on Oct 18, 2021 ContributorMore actionsIssue template for bug reports. Although this looks more like a question, better suited for sites like StackOverflow.
It is created by
Reference in new issuefeature of GitHub from #45884 (comment).synckithas alib/index.d.tswhich matches up tolib/index.js- its esm entrypoint. However, you're importing from a cjs mode file, so the exports map isn't resolving to that, it's resolving tolib/index.cjsinstead, and there's nolib/index.d.ctsto describe it, hence the error.synckitcan either provide alib/index.d.ctswith its' cjs entrypoint shape, or it can provide atypescondition that overrides the types we look up.@angular/compileris esm only - your errors tell me you're importing it in a cjs mode file. So that's an issue. Second,@angular/compileris borked. What do I mean by that? Their declaration files are esm mode (as indicated bypackage.json, but their internal imports don't include any extensions! Theirindex.d.tsis justexport * from "./compiler"-"./compiler"doesn't resolve under esm resolution rules, hence why there's noparseTemplatemember. Whoever went and added thetypescondition to their export map didn't actually check that their types were esm resolution compatible.Reacted by Ray FossReacted by Brian KimWesley Wigham (@weswigham) Thanks for reply.
I tried the following locally for
synckitinnode_modules:{ "exports": { "import": "./lib/index.js", "require": "./lib/index.cjs", "types": "./lib/index.d.ts" } }This still does not work, is there anything wrong?
src/index.ts:2:30 - error TS1471: Module 'synckit' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead. 2 import { createSyncFn } from 'synckit' ~~~~~~~~~ src/worker.ts:2:29 - error TS1471: Module 'synckit' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead. 2 import { runAsWorker } from 'synckit' ~~~~~~~~~
@angular/compiler is esm only - your errors tell me you're importing it in a cjs mode file. So that's an issue.
But I'm using
await import()in commonjs.Did you mean something like
export * from "./compiler" - "./compiler.js"is required?- added a commit that references this issue
on Oct 19, 2021 I tried to use
worker.ctsinstead ofworker.tswith"type": "module"today, the errors fromsynckitreduced, but there is still:src/worker.cts:1:29 - error TS1471: Module 'synckit' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead. 1 import { runAsWorker } from 'synckit'I've tried the following in
node_modules/synckit/package.json{ "exports": { "import": "./lib/index.js", "require": "./lib/index.cjs", "types": "./lib/index.d.ts" } }I still don't understand how to fix this part.
See https://github.com/JounQin/test/tree/ts_esm for reproduction
cc Wesley Wigham (@weswigham) Daniel Rosenwasser (@DanielRosenwasser)
Reacted by ZHAO Jin-Xiang and Matttypesmust appear above the other conditions in theexportsmap.(But actually, since there's a separate cjs and esm entry point, I'd delete the
typescondition entirely and just add anindex.d.cts)I've made a note to discuss options for validating / issuing suggestions/diagnostics on package.json files, because that seems like a very easy mistake to make.
In the meantime, it sounds like things are working as expected here. Wesley Wigham (@weswigham) correct me if I’m wrong, but I think we can close this? JounQin (@JounQin) questions/discussion is welcome to continue, just trying to parse this to see if we need to assign out any immediate work.
More direct discussion of export map priorities being a footgun at #46334
Yeah, I don't think there's anything actionable for us here.
typesmust appear above the other conditions in theexportsmap.Andrew Branch (@andrewbranch) Wesley Wigham (@weswigham)
I tried
{ "main": "./lib/index.cjs", "module": "./lib/index.js", "exports": { "types": "./lib/index.d.ts", "import": "./lib/index.js", "require": "./lib/index.cjs" }, "types": "./lib/index.d.ts" }But it still does not work.
So
index.d.ctsseems to be the only option? But how can I produceindex.d.ctswithindex.d.tsat the same time withtsconly?- added a commit that references this issue
on Dec 3, 2021 RebeccaStevens commented
on May 18, 2022 More actionsYeah, I don't think there's anything actionable for us here.
From 4.7 rc's release notes (for refernce)
// package.json { "name": "my-package", "type": "module", "exports": { ".": { // Entry-point for `import "my-package"` in ESM "import": { // Where TypeScript will look. "types": "./types/esm/index.d.ts", // Where Node.js will look. "default": "./esm/index.js" }, // Entry-point for `require("my-package") in CJS "require": { // Where TypeScript will look. "types": "./types/commonjs/index.d.cts", // Where Node.js will look. "default": "./commonjs/index.cjs" } } }, // Fall-back for older versions of TypeScript "types": "./types/index.d.ts", // CJS fall-back for older versions of Node.js "main": "./commonjs/index.cjs" }
It seems that all exports CJS exports using the
.cjsextension must have their own type declaration files with a corresponding extensions. So every.cjsexport must have an accompanying.d.cts; They cannot use a.d.tsfile.So the following doesn't work:
"exports": { ".": { "types": "./index.d.ts", "import": "./index.mjs", "require": "./index.cjs" } }
Nor does this
"exports": { ".": { "import": { "types": "./index.d.ts", "default": "./index.mjs", }, "require": { "types": "./index.d.ts", "default": "./index.cjs", } } }
Which means that if you want to use the same types for both your ESM and CJS exports, you'll need to make a dummy type file like this:
export * from "./index"; export { default } from "./index";
Edit: That work around doesn't work. You'd need to fully dupe the whole file and all sub files.
Wouldn't it be nicer to just allow
.d.tsfiles to be used with.cjsfiles?Nope! The format of the file is important information to the type system - it tells us how the module is loaded and, importantly, if there's a default that's the shape of the module itself when imported in esm, and if it's an error to load it at all in cjs. At runtime a file can't be both an esm and cjs module, and thus neither can the types for a module. (Though, as you've observed, nothing stops you from pulling almost the whole definition of one of the formats from the other one, in the same way you can re-export the actual implementation!)
Reacted by Brian KimRebeccaStevens commented
on May 18, 2022 More actionsThough, as you've observed, nothing stops you from pulling almost the whole definition of one of the formats from the other one
I tried this and all though TS no longer complains about the initial import; within that file TS complains about the imports and refuses to resolve them, so you just end up with
anytypes.within that file TS complains about the imports and refuses to resolve them, so you just end up with any types.
Is that not just because node es module imports require full paths and explicit file extensions (likely .js) to resolve?
RebeccaStevens commented
on May 20, 2022 More actionsIs that not just because node es module imports require full paths and explicit file extensions (likely .js) to resolve?
So I have the following types pre 4.7:
// index.d.ts export { default } from "./foo"; export * from "./bar"; // type only exports
I tried making the following types for 4.7 to go along side them:
// index.d.mts export { default } from "./index.js"; export * from "./index.js";
// index.d.cts import Foo from "./index.js"; export = Foo;
But when using TypeScript 4.7-rc, TS complains about the exports (imports) in
index.d.ts(due to not having a.jsextensions). Is it possible to keepindex.d.tscompatible with old versions of TypeScript that don't support the.jsextension? Or will I have to duplicate all the type files to support both current and legacy type resolving? (Or will all relatively recent TS version understand the extension?)Also, when using
.cjsand.mjsfiles in vs-code insiders, TypeScript successfully finds all the types and doesn't complain about the missing extensions; that's only happening in.ctsand.mtsfiles. (I havetsconfig.jsonandjsconfig.jsonboth setting"moduleResolution": "Node16").On a sidenote, is it possible for me to re-export the type only exports in the- Actually this probably isn't necessary as types can't be imported via.d.ctsfile while keepingexport = Foo?require.Is it possible to keep index.d.ts compatible with old versions of TypeScript that don't support the .js extension?
.ts(and.d.ts) files adopt the same mode as.jsfiles in their given context, so in atype: modulefolder, they'll be interpreted as esm (and thus require extensions on imports in them). You can use atypesVersionspackage override (or versionedtypesexport map conditions) to provide a specific entry point for older TS (or newer TS) which does not (or does) respect that.Or will all relatively recent TS version understand the extension?
But every version of ts ever (that has a commonjs resolver) actually allows keeping the (js) extension on the import, so you probably don't need to do much other than add extensions.
RebeccaStevens commented
on May 21, 2022 More actionsOki, that's good to know. It my be worth mentioning that in 4.7's release notes as I imagine quite a few other people will also have this misunderstanding as up until now, extensions have pretty much always been left off for TS/JS file imports.
- locked as resolved and limited conversation to collaborators
on Oct 21, 2025
I just tried the
typescript@nextandmoduleResolution: 'node12'with@angular/compiler@v13, buttsc -bfailed to build:@angular/compiler@v13is ESM only,ParsedTemplateis typing exported from itstypesentry.See https://unpkg.com/browse/@angular/compiler@13.0.0-rc.0/package.json
synckitis both commonjs and ESM compatible.See https://unpkg.com/browse/synckit@0.6.0/package.json
I have no idea how can it be fixed on my side.
Test source codes:
Originally posted by JounQin (@JounQin) in #45884 (comment)