You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
tsc ignores errors in an .mjs module with the same name as an .mts moduleΒ #47796
// @ts-check/** @typedef {import("a").default} A */
a.mts:
importbfrom"b";
Run in Terminal (after npm install):
npm run types
π Actual behavior
The VS Code editor correctly shows the TypeScript type errors in both a.mjs and a.mts:
The tsc CLI only shows the a.mts error, and incorrectly ignores the error in a.mjs.
It doesn't matter if the sibling .mts file with the same name has an error or not; just it's presence causes errors in the .mjs file to be hidden:
π Expected behavior
The tsc CLI should display all TypeScript type errors in project modules, regardless if any have the same name as a sibling with a different extension.
This is working as intended. The JavaScript file is considered a build artifact of the TypeScript file, it's just output.
But a.mjs is not a build artifact, and TypeScript shouldn't assume that it is. All of my type safe TypeScript v4.6-dev.x supporting packages have zero build step; I write by hand the exact .mjs and .mts modules I want to publish in package exports. Both types of modules contain types (via JSDoc comments for .mjs) that need to be type checked as I'm editing them via VS Code and in GitHub Actions CI via the tsc CLI.
The project jsconfig.json has compilerOptions.noEmit configured to true and even if the project did have a build step, why would tsc assume the a.mjs file came from the sibling a.mts file? Builds can be configured to place the compiled files anywhere. Within .mjs and .mts modules ESM import paths must specify the file extensions, so the files being siblings has no special meaning as no tools should ever be trying to resolve one or the other from extensionless import paths.
The sibling a.mjs and a.mts modules both being published package exports is not a particularly exotic approach; for example a.mjs could be for browser projects to deep import and a.mts for Deno projects to deep import. Both need to be type checked independently and could have quite different contents.
Even if I am mistaken about any of the above, why would VS Code show type errors in a.mjs, if it's just a "build artifact" and should be ignored for type checking? VS Code has all the same config and information that the tsc CLI has yet they disagree on the errors, so surely there is a bug somewhere here.
Out of curiosity, I tried configuring compilerOptions.outDir to "dist" in jsconfig.json, so tsc should definitely know that a.mjs is not an artifact of the sibling a.mts yet still the bug persists; any type errors in a.mjs are ignored by the tsc CLI.
Ok, so compilerOptions.outDir (after removing compilerOptions.noEmit) doesn't apparently do anything within jsconfig.json, but it does with tsconfig.json.
And with the package types script updated to tsc -p tsconfig.json the dist folder is created, but with only a.mjs in it derived from the a.mts file. It still doesn't report any of the source a.mjs module type errors; it seems to totally skip it. What would be more intuitive is if both a.mjs and a.mts were processed like normal, but then whatever one is processed second overwrites the first compiled a.mjs in the compilerOptions.outDir. That way you get type check error feedback for both. Certainly if compilerOptions.noEmit is true then both should be type checked; the primary use case for that is to type check all the project modules.
It would be great if TypeScript had a simple check-types command that totally avoids compiling modules and friction such as this.
Side note; but node12 (lowercased) is a documented valid value for compilerOptions.module:
Yet VS Code warns this is invalid, and suggests Node12 (uppercased):
Only for tsconfig.json though; VS Code is happy with node12 within jsconfig.json:
Explicitly list your input files if you actually want to include both - the compiler only automatically pulls in one copy of a file with the same basename (the most TS-y one). (Do note, this will cause other issues like the compiler warning you that they share an emit path if you have emit one!)
Activity
MartinJohns commentedon Feb 8, 2022
This is working as intended. The JavaScript file is considered a build artifact of the TypeScript file, it's just output.
jaydenseric commentedon Feb 8, 2022
But
a.mjsis not a build artifact, and TypeScript shouldn't assume that it is. All of my type safe TypeScript v4.6-dev.x supporting packages have zero build step; I write by hand the exact.mjsand.mtsmodules I want to publish in packageexports. Both types of modules contain types (via JSDoc comments for.mjs) that need to be type checked as I'm editing them via VS Code and in GitHub Actions CI via thetscCLI.The project
jsconfig.jsonhascompilerOptions.noEmitconfigured totrueand even if the project did have a build step, why wouldtscassume thea.mjsfile came from the siblinga.mtsfile? Builds can be configured to place the compiled files anywhere. Within.mjsand.mtsmodules ESM import paths must specify the file extensions, so the files being siblings has no special meaning as no tools should ever be trying to resolve one or the other from extensionless import paths.The sibling
a.mjsanda.mtsmodules both being published package exports is not a particularly exotic approach; for examplea.mjscould be for browser projects to deep import anda.mtsfor Deno projects to deep import. Both need to be type checked independently and could have quite different contents.Even if I am mistaken about any of the above, why would VS Code show type errors in
a.mjs, if it's just a "build artifact" and should be ignored for type checking? VS Code has all the same config and information that thetscCLI has yet they disagree on the errors, so surely there is a bug somewhere here.jaydenseric commentedon Feb 8, 2022
Out of curiosity, I tried configuring
compilerOptions.outDirto"dist"injsconfig.json, sotscshould definitely know thata.mjsis not an artifact of the siblinga.mtsyet still the bug persists; any type errors ina.mjsare ignored by thetscCLI.jaydenseric commentedon Feb 8, 2022
Ok, so
compilerOptions.outDir(after removingcompilerOptions.noEmit) doesn't apparently do anything withinjsconfig.json, but it does withtsconfig.json.With this
tsconfig.json:{ "compilerOptions": { "allowJs": true, "checkJs": true, "module": "Node12", "strict": true, "outDir": "dist" }, "typeAcquisition": { "enable": false } }And with the package
typesscript updated totsc -p tsconfig.jsonthedistfolder is created, but with onlya.mjsin it derived from thea.mtsfile. It still doesn't report any of the sourcea.mjsmodule type errors; it seems to totally skip it. What would be more intuitive is if botha.mjsanda.mtswere processed like normal, but then whatever one is processed second overwrites the first compileda.mjsin thecompilerOptions.outDir. That way you get type check error feedback for both. Certainly ifcompilerOptions.noEmitistruethen both should be type checked; the primary use case for that is to type check all the project modules.It would be great if TypeScript had a simple
check-typescommand that totally avoids compiling modules and friction such as this.Side note; but
node12(lowercased) is a documented valid value forcompilerOptions.module:Yet VS Code warns this is invalid, and suggests
Node12(uppercased):Only for
tsconfig.jsonthough; VS Code is happy withnode12withinjsconfig.json:weswigham commentedon Feb 8, 2022
Explicitly list your input
filesif you actually want to include both - the compiler only automatically pulls in one copy of a file with the same basename (the most TS-y one). (Do note, this will cause other issues like the compiler warning you that they share an emit path if you have emit one!)