TypeScript: Intellisense not resolving modules when using `exports` in package.json

Reposting from VSCode repo for @magnusriga. I transferred the original issue to the wrong repo and now can’t transfer it back


Does this issue occur when all extensions are disabled?: Yes

  • VS Code Version: 1.84.2
  • OS Version: Windows 11 Pro

Steps to Reproduce:

  1. Build any project (A) with a dependency on another project (B), where project B’s package.json exports files using the exports field
  2. Observer how Intellisense does not work
  3. Insert the main and types fields and see that Intellisense starts working again

Intellisense Works When main and types are set:

Package B’s package.json

image

Package A’s import (notice the Intellisense pop-up. It is possible to ctrl-click the path.)

image

Intellisense Does Not Work When only exports is set:

Package B’s package.json (removed main and types)

image

Package A’s import (not possible to crl+click to find the package, i.e. no Intellisense)

image

About this issue

  • Original URL
  • State: closed
  • Created 8 months ago
  • Reactions: 1
  • Comments: 34 (14 by maintainers)

Most upvoted comments

If so, how do I do it?

Update to TypeScript 5.4 when it comes out. #56946

Yes, I’ll try that out and see if there are any objections.

One other thing I noticed which may also be a bug

This is working as intended. JavaScript files imported from node_modules are not analyzed for type information by default.

Subpaths in exports also seem to break IntelliSense (auto-complete of RHS import paths does not work)

This appears to be a bug.

That’s correct, at least according to the exports spec written by Node.js, which is what TS tries to follow. Some bundlers may be more flexible than this but there is some ongoing discussion about making those bundler resolvers conform to the spec in future major version bumps. Neither option is better practice than the other. It’s up to you.

@virtuallyunknown What you say makes complete sense, but I couldn’t find any other good way to remove that ESLint error you mentioned, which I got when importing a JS file into a TS file. Obviously the JS file does not have type declarations in the first place, so ideally it should not check that file when it sees that it is JS and not TS.

Other suboptimal ways the remove the error included changing strict to false in tsconfig, or placing @ts-expect-error above the import. None of these seem viable.

What’s happening here is the presence of the tsconfig.json next to test.js is throwing us off from finding the root jsconfig.json when looking for a config to use for editor support. When you open test.js, we start looking for jsconfig/tsconfig files up the directory spine from that file. We stop when we see the tsconfig.json, but it doesn’t actually include test.js. Instead of continuing the search, it seems like we give up and put test.js in an inferred project with default settings. I don’t know if this is a known limitation or a bug, but it seems like something that could be improved. (cc @sheetalkamat)

I think the most conventional workaround would be to put your scripts in their own directory with their own tsconfig.json/jsconfig.json. Alternatively, you could name your package-level tsconfigs something else (tsconfig.src.json) and reference them from a root-level solution tsconfig.json:

{
  "files": [],
  "references": [
    { "path": "./packages/two/tsconfig.src.json" },
    { "path": "./tsconfig.scripts.json" }
  ]
}

The trick is to make it so that the nearest file named literally tsconfig.json (or jsconfig.json) up from any file actually includes that file somehow, either directly or by fanning out into a referenced project that includes it.

@andrewbranch thanks for taking your time to explain the issue to me, I really appreciate it.

I have tried the jsconfig.json and it seems to do the trick, so I suppose this is an acceptable workaround for the time being (or alternatively using main field), and if you and/or the vscode team are aware of the issue, then that’s great.

In my personal opinion it’s important that developers can make use of all ESM features so we can finally move away from CJS and all start using one unified system, but I also understand you guys probably have more pressing issues that require your attention.

Cheers!

@virtuallyunknown this is a known issue regarding the default settings that VS Code passes to TS Server (which powers JavaScript IntelliSense). The current defaults are very forgiving, which is good because we don’t know what kind of project a JS user has without additional config, but they lack support for newer features like package.json "exports". If this is a Node.js project, you can add a jsconfig.json file to your project root like this:

{
  "compilerOptions": {
    "module": "nodenext"
  }
}

@andrewbranch I actually got it to work. I posted a working repo above to showcase the settings needed to make it work (in case it is useful to others in the future).

@mjbvz can mark it as resolved (I am unable to, since I am no longer the OP).