-
-
Notifications
You must be signed in to change notification settings - Fork 2
Fix type imports #3
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
base: main
Are you sure you want to change the base?
Conversation
ESM imports require a file extension. Also directory imports don’t resolve to `index.js`. This goes for source code as well as type definitions. Modern module resolution settings validate this. The correct `"moduleResolution"` value for libraries that target Node.js 16 or lower is `"Node16"`. For libraries that target Node.js 18 or greater should set it to `"Node18"` or `"NodeNext"`. For code that gets bundled, the correct option is `"Preserve"`. All other options are outdated. The config in `@ljarb/tsconfig/node` sets this correctly, but it contains a syntax error.
| @@ -1,3 +1,3 @@ | |||
| import getAsyncFunction = require('.'); | |||
| import getAsyncFunction = require('./index.js'); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is using require, so it's not an ESM import.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your reasoning makes sense, yet I see a type error.
node_modules/async-function/index.d.mts:1:35 - error TS7016: Could not find a declaration file for module '.'. 'node_modules/async-function/legacy.js' implicitly has an 'any' type.
1 import getAsyncFunction = require('.');
~~~
Found 1 error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| import getAsyncFunction = require('./index.js'); | |
| import getAsyncFunction from './index.mjs'; |
this should also work, but fwiw this seems like a typescript bug
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your suggestion contains a circular type definition.
The following works, but only if allowSyntheticDefaultImports is enabled:
import getAsyncFunction from './index.js';I agree this seems like a TypeScript bug. But still, this PR as-is solves the problem, also for current TypeScript versions where the bug is present.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TS's module system is broken if allowSyntheticDefaultImports isn't enabled, so I'm not too worried about that - everyone should always have that enabled, full stop.
With either change, though, i'd love some way to ensure it doesn't regress. Why didn't TS catch this locally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TS's module system is broken if allowSyntheticDefaultImports isn't enabled
This is only true for CJS / ESM interop.
With either change, though, i'd love some way to ensure it doesn't regress. Why didn't TS catch this locally?
You’re using "moduleResolution": "classic". You should never use this. Always set it to "Node16" (if targeting Node.js < 18) / "Node18" (if targeting Node.js >= 18 / "NodeNext" (if you’re less concerned about future updates) for libraries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but most of the package is a CJS library, which should be using classic resolution, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that’s a common misconception. The Node16 (and greater) module resolution handles both CJS, ESM, and dual publishing.
Now that I’m thinking about it, you could test against multiple module resolutions using multiple tsconfigs and TypeScript project references. This would allow you to test against the Node10 module resolution too. I don’t think I’ve seen people do this before, but you could do this.
As for the Classic module resolution: I don’t know its details too much. This was already outdated when I started using TypeScript.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, according to the comments left by tsc --init, i'm using node10, not classic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to tsc --showConfig you left it unset. According to the documentation this means moduleResolution defaults to Node10 for module CommonJS. So you’re right, I was mistaken. :)
ESM imports require a file extension. Also directory imports don’t resolve to
index.js. This goes for source code as well as type definitions.Modern module resolution settings validate this. The correct
"moduleResolution"value for libraries that target Node.js 16 or lower is"Node16". For libraries that target Node.js 18 or greater should set it to"Node18"or"NodeNext". For code that gets bundled, the correct option is"Preserve". All other options are outdated.The config in
@ljarb/tsconfig/nodesets this correctly, but it contains a syntax error.