-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Module resolution: d.ts
files do not get treated depending on the importer's type
in package.json, causing issues with resolution-modes
#56455
Comments
Not only is TypeScript working as intended here, the types for these packages appear to be plausibly correct, and warning you of a real instance of the dual-package hazard.
The types resolve to two different declarations of the class If
This has never been the case, and any implementation that did this while trying to model Node.js would be incorrect. |
Interesting... this hasn't been an issue for a while, and only recently creeped up, I guess there were some changes in how file resolutions worked / how Also, do you have any tips on
Would that count as a separate issue? I could probably try to build an example module showcasing that issue in particular if needed 👀 |
Since we're dealing with class A1 {
#foo = 42;
go(a) {
a.#foo;
}
}
class A2 {
#foo = 42;
go(a) {
a.#foo;
}
}
const a1 = new A1();
const a2 = new A2();
a1.go(a1); // ok
a1.go(a2); // runtime error
a2.go(a1); // runtime error
a2.go(a2); // ok Which makes the dual-package hazard even more of a footgun. |
It has been possible to get yourself into this exact situation since TypeScript 4.7; minor things have been fixed and tweaked but I can’t think of anything that would have suddenly made this more likely for you. It seems more likely that those Discord libraries were updated in a way that made this blow up.
There’s no such thing as a “universal” declaration file; tsup’s single-.d.ts output was always incorrect. I fixed it a few months ago by making it emit separate It looks like the import http = require('@discordjs/core/http-only');
import discord = require('discord.js');
const rest = new discord.REST({ version: '10' }).setToken('');
const api = new http.API(rest); |
We're trying to fix this for consumers of
Technically no, realistically yes. The contents of the files are identical to this day, not to say that they shouldn't be separate files right now (specifically because of issues like you pointed out already). But in the PR where you fixed it by having separate outputs you neglected to ensure that any chunks generated also got this treatment. We had at least one release cycle that contained these chunks (a single .d.ts alongside our normal multiple entrypoints with d.mts and .d.ts) without these errors coming up. Essentially, what the compiler should've seen was app imports You are correct that for |
Because a declaration file informs the compiler about the existence of and module format of exactly one JavaScript file, and misrepresenting the module format of the JavaScript file will lead the compiler to issue incorrect errors or type imports incorrectly. Also, it’s how this exact dual package hazard issue was caught. If these libraries had tried to fudge the reality and share a single “universal” declaration of |
So what are we supposed to do for type only files? If a declaration file represents one js file, then type only files clearly mustn't exist. But hey, say we made one anyways, it works great up until you import a dual package into said file. This seems random and is super unintuitive, especially considering the errors it generates look like typescript is saying the exact same thing is not assignable to itself (because most beginner-intermediate devs neglect the 3 words that indicate that the module resolution occurred differently in the massive error generated) |
You're not supposed to use If your type-only |
Not always. The typescript compiler, which of course doesn't support dual packages anyways, does this. A bundler will either completely remove the file from the output or will bundle it directly in as an empty file. |
I don't know what to tell you except that 1) it's been stated by the TS devs that With regard to hypothetical dual package support in TS itself, refer to #54593. |
While a module declaration file always implies the existence of a module JavaScript file, practically speaking, if you have a dual package and want to share type definitions between both formats, it’s currently a better choice to make them CommonJS format, because those can be easily imported by both formats. If you make your types-only file look like it represents an ESM JavaScript file, you will have to import it like this in your CommonJS package: import type { Types } from "../shared/types.mts" with { "resolution-mode": "import" } which is only supported in TypeScript 5.3+. |
Yes, now we’re on the same page https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html#considerations-for-bundling-libraries I encourage people not to bundle libraries because of these issues. I’ve talked with Evan You about fixing this in Rolldown by giving it first-class declaration emit support. The problem with existing solutions is that declaration emit is being done totally separately from the bundler’s core work, so it’s not possible to coordinate them. |
This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
Demo Repo
https://github.com/vladfrangu/ts-resolution-issue
Which of the following problems are you reporting?
Something else more complicated which I'll explain in more detail
Demonstrate the defect described above with a code sample.
Run
tsc --showConfig
and paste its output hereRun
tsc --traceResolution
and paste its output hereSee https://github.com/vladfrangu/ts-resolution-issue/blob/main/resolutions.txt (couldn't paste it due to size)
Paste the
package.json
of the importing module, if it existsPaste the
package.json
of the target module, if it existsAny other comments can go here
At first, we assumed that the issue was that
discord.js
lacks anexports
key, but as you can see by runningnode itShouldWorkWithThisOnly.mjs
in the cloned repository (after installing dependencies), that isn't the case.The only way to make the error not happen is by having an
d.mts
file and passing it into theimport
key for the exports (runitWorksNowTho.mjs
in the cloned repository)This used to not be needed. Before, TSC would treat
d.ts
files according to whatever export condition it matched (so, if it was in animport
condition -> ESM,require
->CJS
, etc). It seems thatresolution-mode
has made this much stricter, or possibly even outright broke behavior.This has caused discordjs/discord.js#9985 and has required us to alter our build pipeline for our other packages (tsup used to generate a "shared"
d.ts
file which contained duplicated typing declarations, but ever since resolution became stricter (or broke? not sure), it was always interpreted as aCJS
declaration instead of an universal oneThe text was updated successfully, but these errors were encountered: