Repository navigation
module: esnext should append .js to imports #33588
Description
Activity
- addedWorking as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
on Sep 25, 2019 RyanCavanaugh commented
on Sep 25, 2019 MemberMore actionsTypeScript always emits JavaScript code as written, and import statements are JavaScript code so aren't changed on emit. You should always write the import path that you want to appear in the emitted JS file.
Ryan Cavanaugh (@RyanCavanaugh) hi give me a chance before closing...
- typescript code can have different compilation targets.
target+moduleintsconfigshould determine and act differently when I'm using:
1.1. commonjs don't touch extensions
1.2 nodejs es6 module withouttype: modulemeaning it should have.mjsextensions
1.3 browser es6 and nodejstype: modulepackage meaning it should have.jsextensions - I may actually compile in-place. Having hard-coded
.jsextension in typescript files will mean I'm importing previously generated '.js' files and those will be analysed instead of the imported typescript files.
Reacted by nuts-n-bits and Alexis- typescript code can have different compilation targets.
RyanCavanaugh commented
on Sep 26, 2019 MemberMore actionsWe read comments on closed issues and you're able to comment on them; open/closed is simply an indicator of whether there's engineering work that needs to happen next. As I indicated, this is the intentional behavior of TypeScript, so there's no next steps in terms of code changes.
Those are all problems with JavaScript and should be addressed by whatever tool you would have fixed it with if you weren't using TypeScript. One of TypeScript's key design goals is that TypeScript always emits JavaScript code as written, and import statements are JavaScript code so aren't changed on emit.
Hi thank you for the responding, but I really just want to note that I still don't see how problem 1 is different from other problems addressed by typescript. For example, compiling imports to commonjs in the first place. It is just downleveling according to the
tsconfig'starget(unfortunately, some targets have different extension handling)Regarding problem 2, I don't know how it can be addressed outside of typescript, maybe I'm missing something (of course avoiding in-place compilation is an option, but it should not be a limitation imposed by typescript IMO)
and problem 3, even if I'm not compiling in place, using
.jsextension in import statement will fail since thejsfile is not there before compilation.Ryan Cavanaugh (@RyanCavanaugh) this is a real issue, I really think it should be seriously addressed at the compiler level. But if it isn't there should be at least clear guidelines/practical example, for using typescript with node's experimental-modules (with or without
type: module) as well as with browser modules.Reacted by Radovan ObalFor those ending up here from Google looking for a solution:
Write
import X from "./X.js"(so ending in.js) in.ts(x)files, when outputting ESM.
This is because the compiler's output will go to X.js and as mentioned above, import specifiers are not modified by TypeScript, so it won't add.jsfor you neither will it change.tsto.js.Reacted by Brady Holt, Teslim Olunlade and Jan Molakpvarga-dni this doesn't solve the issues I've mentioned above. Just motivates a reopening of this issue. As you said, Typescript will output the import string as is and during development will use the wrong file in in place compilation (.js instead of .ts).
Ryan Cavanaugh (@RyanCavanaugh)Reacted by Radovan Obal and jasonburrowsI understand that this is working as intended, but I was wondering if anyone could clarify the intent.
I don't think it's very intuitive to import a path that doesn't exist, even if it's probably clear that
.tswill get turned into.jsby the compiler.Furthermore, as outlined above, what it I wanted to output
.mjsfiles?Finally, bundlers are a thing. When bundling, the module
.jsfile is never actually emitted. The mental gymnastics to understand that there would have been a.jsbut it's actually getting bundled seem unnecessary to me.To align more closely with how Node handles ESM (ignoring experimental module resolution for a sec), what would be the actual harm in
importing a.tsmodule from the source and rewriting the path to.jsat compile time?Migrating to an all-ESM world carries enough complexity as it is. The compiler could do some of the heavy lifting rather than making it worse.
Reacted by Amit Portnoy, Philipp G., Toby Wicks, Michael Hewitt, lgpasquale, Branco Bruyneel, Alexis, Sehrish Waheed, parsehex, Jane Jeon and 9 more- added 2 commits that reference this issue
on Jul 23, 2022 - locked as resolved and limited conversation to collaborators
on Oct 21, 2025
TypeScript Version: 3.4.0-dev.201xxxxx
Search Terms:
modules
Code
having a local-lib.ts
index.ts
should compile with
"module": "esnext"to (probably in combination with new extension rewriting flags):Expected behavior:
.jsshould be appended to imports of local es modules(this is required in node and browser modules support)
Actual behavior:
no
.jsPlayground Link:
Related Issues: