Skip to content

Unresolved TypeScript definition paths (when using node-esm with imports) #48639

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

Closed
mannie-exe opened this issue Apr 11, 2022 · 10 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@mannie-exe
Copy link

mannie-exe commented Apr 11, 2022

Hey there!

Bug Report

I'm writing a small, shared module to be imported in other TypeScript programs and I noticed a slight error with my generated TypeScript definitions (that might not be a bug, yet I'm unsure of how to fix).

I wanted to be able to reference my src folder in my project root as #src, so I added the required imports and paths in my package.json and tsconfig.json respectively.

After letting tsc generate my JavaScript, I'm able to run the project just fine -- however, importing my library in another TS project showed me that the TypeScript definitions were also using the Node.js-like #src path, but were obviously not resolving properly and couldn't be loaded.

What would be my best course of action to fix this? I understand that this type of module resolution is just something Node.js's doing (based off of packge.json/imports), so what would TypeScript's solution be to projects using this feature? Can I use custom, local imports in my package.json yet resolve emitted type definitions correctly?

🔎 Search Terms

package.json esm imports type declarations emitted path incorrect resolve

🕗 Version & Regression Information

  • This can be a crash if: an ESM-based Node.js package, with imports, generates types that are consumed anywhere
  • This has only been tested on the [email protected] nightly build

💻 Code

Here are the relevant bits from my package.json file:

{
  "type": "module",
  "exports": {
    ".": {
      "types": "./dist-types/index.d.ts",
      "import": "./dist-esm/index.js"
    }
  },
  "imports": {
    "#src/*": [
      "./dist-esm/*.js"
    ]
  },
  "devDependencies": {
    "typescript": "^4.7.0-dev.20220408"
  }
}

...and tsconfig.json file:

{
  "compilerOptions": {
    "lib": [
      "es2022"
    ],
    "target": "es2022",
    "module": "es2022",
    "moduleResolution": "node"
    "esModuleInterop": true,
    "baseUrl": ".",
    "paths": {
      "#src/*": [
        "./src/*.ts"
      ]
    },
    "outDir": "./dist-esm",
    "declarationDir": "./dist-types",
    "declaration": true,
    "declarationMap": true
  }
}

🙁 Actual behavior

The resulting type declarations, ./dist-types/index.d.ts in my project for example, has imports such as:

import { AuthMessageType, LoginRequestMessage, LoginSuccessMessage, LoginFailureMessage } from '#src/messaging/auth';

...and while generated JS in ./dist-esm resolves #src just fine, TypeScript has no idea where to find the rest of my type definitions.

🙂 Expected behavior

I'm not sure to be honest. The TypeScript config's paths definition is technically working just fine, as my source files are mapped correctly -- what would the equivalent, or generally correct way of fixing this for just the type definitions be?

@IllusionMH
Copy link
Contributor

"module": "es2022", "moduleResolution": "node" doesn't have support for Node.js ESM features. You should use node12 or nodenext instead
https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#ecmascript-module-support-in-node-js

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Apr 11, 2022

Is it possible to warn a user when resolution fails under --moduleResolution node but we find a package.json that has an "exports" field but not a "main", "type", or index.js?

@RyanCavanaugh
Copy link
Member

@weswigham thoughts?

@weswigham
Copy link
Member

If resolution outright fails, yeah, we should be able to. Most package authors have a main/types fallback, though.

@mannie-exe
Copy link
Author

mannie-exe commented Apr 11, 2022

"module": "es2022", "moduleResolution": "node" doesn't have support for Node.js ESM features. You should use node12 or nodenext instead https://devblogs.microsoft.com/typescript/announcing-typescript-4-7-beta/#ecmascript-module-support-in-node-js

Hey! So if my package.json defines #src/* as ./dist-esm/*.js -- how will my generated TypeScript definitions resolve them with just the tsconfig.json (with paths as configured above)? Setting my module resolution to nodenext wasn't enough, as my generated source files with #src/* imports were already working just fine. The issue arises when my TypeScript definition files start doing the same.

./dist-esm/index.js: WORKS FINE ALREADY ✔

import { AuthMessageType, LoginRequestMessage, LoginSuccessMessage, LoginFailureMessage, } from '#src/messaging/auth';

./dist-types/index.d.ts: DOES NOT WORK FINE ❌

import { AuthMessageType, LoginRequestMessage, LoginSuccessMessage, LoginFailureMessage } from '#src/messaging/auth';

Another module importing this library shows all imported types as any -- I'm guessing because my TypeScript definitions are still resolving #src to ./dist-esm/*.js as per the Node package configuration instead of ./dist-types/*.d.ts.


I still don't know what the best solution might be, but my brain keeps falling back to the idea of another paths-like field that mirrors the paths entry but resolves them with type definition-specific paths, i.e.

{
  "compilerOptions": {
    "paths": {
      "#src/*": [
        "./src/*.ts"
      ]
    },
    "typeDefPaths": {
      "#src/*": [
        "./dist-types/*.d.ts"
      ]
    }
  }
}

-but I don't know how philosophically compatible or elegant this solution would be.


EDIT: Admittedly, I feel like I'm using the whole system improperly right now. I just wanted a nice, clean method for absolutely referencing sources files from deep within internal modules (at mostly like -- a couple files). It won't be a big deal to switch to relative-only imports (I already have on another branch), but I'm just curious to know what the intended solution be for a situation where the TypeScript definitions and source files need their import paths to be resolved differently. What would TypeScript do?

@DanielRosenwasser
Copy link
Member

I appreciate you stress testing these features!

My personal recommendation would be to not split out your .d.ts and .js file emit; however, if that ends up being our only recommendation for library authors, I don't really think there's a reason for any library author to use the "imports" field (while it would still have some utility for application developers).

@weswigham
Copy link
Member

weswigham commented Apr 11, 2022

@schmannie You just need #47925 - your non-relative imports point to the output location for your project (correctly), but TS currently isn't smart enough to remap those output files back to the program's input files for nonrelative imports (the workaround being, ofc, to use relative imports for input files for the time being - it's actually a very very longstanding bug that's only becoming critical now that import maps exist at runtime).

@mannie-exe
Copy link
Author

mannie-exe commented Apr 12, 2022

I appreciate you stress testing these features!

My personal recommendation would be to not split out your .d.ts and .js file emit; however, if that ends up being our only recommendation for library authors, I don't really think there's a reason for any library author to use the "imports" field (while it would still have some utility for application developers).

I understand. Thank you very much! I've just opted to switch to relative imports as @weswigham suggested. Looking forward to seeing how #47925 pans out! Let me know if I should do anything to this issue in the meanwhile.

@RyanCavanaugh
Copy link
Member

Tracking as duplicate of #46762. Thanks!

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Apr 12, 2022
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

6 participants