-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Compiled JavaScript import is missing file extension #40878
Comments
TypeScript doesn't modify import paths as part of compilation - you should always write the path you want to appear in the emitted JS, and configure the project as necessary to make those paths resolve the way you want during compilation |
Thanks @RyanCavanaugh! Do you mean I should write However, I find this surprising, because
|
TS never takes an existing valid JS construct and emits something different, so this is just the default behavior for all JS you could write. Module resolution tries to make all of this work "like you would expect" See also #15479 (comment) |
@RyanCavanaugh that principle makes sense. My remaining confusion then is why the docs and compiler encourage omitting the file extension, if "you should write the import path that works at runtime". Maybe this encouragement is designed for a JS runtime that demands that you omit the file extension. But the runtimes I'm aware of are browser, ES modules, and Node.js, none of which make this demand. |
@RyanCavanaugh Writing "./dep.js" doesn't sound logical. The file dep.js does not exist in the Typescript universe. This approach requires the coder to know the exact complied output and be fully aware of the compiled environment. It's like having to know the CIL outputted and modify it here and there in order to code in C# successfully. Isn't the whole idea of Typescript to abstract away Javascript?
IMHO, this issue should be a bug. |
The whole idea of TypeScript is to add static types on top of JavaScript, not to be a higher-level language that builds to JS. The C# / IL comparison is not apt at all. There's literally no line of undownleveled JavaScript code you can write where TS intercepts some string in it and changes it to be something else. This is 100% consistent with TS behavior in every other kind of JS construct; it would be frankly bizarre to have import paths be the one thing that we decide to go mess with. |
problem is, ts does not only need to add '.js', there are complexer resolution strategies. i would like it, if there would be a way of calling a plugin after transpilation had been done, so I could fix the import names with that. |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
@RyanCavanaugh @jameshfisher I was about to post a new bug about TypeScript but then the template encouraged me to search again, and I found this issue, which might be similar enough. But I'm still confused, even reading above. Here is the "bug" I submitted to the "vscode" repo, and that team rejected it and said that I should submit to the "TypeScript" repo here: microsoft/vscode#108872 It seems that people trying to write TypeScript in VSC face this dilemma of choosing between:
I'm getting this error and don't understand why:
See also: #11235 (comment) |
So far, I have had success with @RyanCavanaugh's suggestion to "always write the path you want to appear in the emitted JS, and configure the project as necessary to make those paths resolve the way you want during compilation". But I am still confused because this advice contradicts the compiler and docs and ecosystem, which all encourage omitting file extensions. |
Yeah that doesn't feel right. I really don't understand. I can't think of any principle where it would make sense to throw a prominent red error for something legal. |
Same issue here. Adding a However, when I add I would love to see the TypeScript compiler add |
@josdejong you can get around this by using an In the index.ts file do the following: In your test just import the file directly instead of via the index file: |
Thanks for sharing your workaround @bobbyg603 . I'm not sure how that would work for the imports inside It feels to me like there is a serious mismatch between plain ES modules (requiring actual existing paths with proper file extensions) and TypeScript code (relying on "smart" nodejs module resolution, not requiring file extensions, and (implicitly) changing the (implicit) file extensions from .ts to .js when compiling). I can't understand this issue is marked "Working as intended" in #40878 (comment), this is a problem that needs to be addressed. Most logical to me would be to write imports with |
Facing the same problem. In most scenarios, we use webpack. But for simple scenarios and samples, e.g. for our TS-training for developers, we want to use plain TS files and JS modules without the complexity of another transpiler like Babel/webpack. This means:
Why not add an option to tsconfig and leave it to the developer to use it or not? I'm aware that "import ./myModule" could resolve to something which is not a static file on the web-server. E.g. a web-service dynamically resolving and returning js-code. In any case, please such an option would leave it to the developer's resonsibility how to transpile it. |
I think |
@ioslh I'm trying to get eslint to be able to ensure ".js" extensions are never forgotten |
TypeScript developers, please, do whatever the hell you want with file extensions, this debate has been going on for years, I don't care, your mind is set, fine. But just document this very basic thing in a place that's easy to find: how on earth do I run the JavaScript emitted by tsc when "module" is set to "ESNext"? That will save a lot of people a lot of time. Most people understand "transpiles to JavaScript" as "transpiles to working JavaScript". But this is not the case. |
I'm hitting this exact same issue. I'm not using webpack, just plain typescript and my javascript output is broken because the .js extension is missing. How on earth can this not be a bug? What am I supposed to do instead? |
@TheBoneJarmer If you're just doing plain TS and JS, use the would-be filename as the import specifier like so: import { version } from "./version.js"; |
The problem with this solution is that if you have an "outDir" different
than the default, i.e. a setup where transpiled files don't live beside the
.ts files, most tools will freak up and that's not a nice development
experience.
I was so pissed-up buy this that I built this monstrosity:
https://www.npmjs.com/package/add-js-extension
You can install it and have it watch your "outDir" and it will quickly and
transparently add the .js extension where appropriate to the transpiled
files as they appear / are updated by tsc.
It seems to be working well. Only missing major feature is that I currently
don't rewrite the source-maps but it doesn't seem to matter much since the
changes I make in the js files are only adding 3 chars to some import
statements at the end of some lines. No line offets, no changes to any
place where you would be likely nor even able to set a breakpoint anyways.
Give it a go and let me know of any issues you might find!
There are webpack modules that do the same thing, but my solution is much
less invasive: keeps file and directory structure, no bundling, no config.
I spent a considerable amount of time reading the obscure ESM spec and I'm
pretty confident my tool doesn't do anything unwise.
I actually think TS has it wrong, as the ESM spec clearly states that there
is no such thing as a default file extension. So you should "import foo
from '. /foo.ts'" and that should transpile to "import foo from
'./foo.js'". I don't see any deviation from JS here. "import foo from
'./foo'" seems just wrong.
…On Thu, Jul 29, 2021, 22:43 RA80533 ***@***.***> wrote:
@TheBoneJarmer <https://github.com/TheBoneJarmer> If you're just doing
plain TS and JS, use the would-be filename as the import specifier like so:
import { version } from "./version.js";
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#40878 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AALESE6S37QUCIGMC3IKOTLT2G4NRANCNFSM4SA23LJQ>
.
|
Thanks for the replies guys. Personally I entirely agree with @djfm. I am grateful for the workaround from @RA80533 though, don't take me wrong. But it is far from a good solution imho. And the reasons why are nicely explained by djfm. That said, you are not the only one who got that idea. I found another npm package through Google which does what I believe you do as well. It is called tsc-esm and is created by user @Gin-Quin. |
That said. I also took my time to learn a bit more about the whole es6 module system and I also think javascript has a flaw here. Imho they should introduce something similar to what happens in urls with index.html. In most if not all web servers you never have to manually insert '/index.html' at the end of an url. When the path ends with a / the server automatically converts that to /index.html. When no file extension is added at the end of an import statement, the client should convert this first to .mjs. If no such file exists than to .js. And only then the client should throw an error if neither files were found. Therefore I more or less can understand why typescript devs don't consider this a bug from their side. Typescript merely interprets the import statement for typescript purposes but may not be able to convert it that easily to esm. But I do think this should be a config option. I mean, several users already created their own npm package out of frustration which I think shows how big this problem is. And they are right to be frustrated because my basic typescript app produces broken javascript right now, which is a definitely a bug from typescript. And the fact that typescript devs do not want to admit that is just plain ignorance and stupid. This will only push people away from using TS. And on top of that, the fact that they say they won't do a thing about it does pisses me off even more. Imho the tsc should stop conversion when an import statement cannot be converted to esm. Or throw a warning or hint that narrows down to "Due to limitations in the javascript language you are supposed to add a .js in your import statement or else tsc will produce broken code". And I'd be fine with that but not the way it is now. |
The recommendation to preemptively append I just encountered this issue while attempting to port a library to TypeScript. I didn’t have to deal with it in prior projects because I was building apps with a bundler. Now that I’m emitting modules that can be tree-shaken downstream, this behavior, intended or not, leaves me in a bit of a pickle. |
@adamshaylor for what it's worth, vitest supports |
Same confusion 🤔 |
good to see this crazy conversation still going after 2 years its amazing to me that something so basic as module resolution continues to be one of the most frustrating things about the nodejs ecosystem I cannot wait for deno to gain more traction and put this issue to bed by enforcing esm to the TS team, listen to your users and add a compiler flag |
I've got a react app that's being bundled with webpack, and a pure typescript server. The 2 projects share some model classes. If I import my classes using the standard Typescript syntax
Alright, fine, I can simply add the ".js": ... This causes webpack errors:
This is infuriating |
At first glance of the time of this issue, I think I'm lucky to find a perfect solution. Until scrolling to the bottom then I realize that I'm unlucky. So sad. |
So weird. In VSC if you Cmd+click on a |
But wasn't it fixed?
To my greatest surprise, in my latest project, I'm using relative imports
in TypeScript without a file extension and the emitted JS just works!
…On Thu, 3 Nov 2022, 15:51 Claudio Cicali, ***@***.***> wrote:
So weird. In VSC if you Cmd+click on a import something from
'something.js'; you'll get to the .ts file, like VSC knows that this
issue exists and "fixes it".
—
Reply to this email directly, view it on GitHub
<#40878 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AALESE7ZQOKDR7GJ3NTCYTTWGPGPFANCNFSM4SA23LJQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
@djfm no, unbelievable |
This has nothing to do with VSC and also works in every other editor with the typescript LSP. Completely unrelated to this issue. |
* feat: initial commit * ci: install prettier * ci: setup git hook for lint-staged * style: formatting * ci: add dependabot config * chore: add generateResetPasswordToken() * chore(package): set node version * ci: remove Github Packages upload * chore(users): remove roles * build: npmignore * build: update eslint, gitignore * build: parcel -> tsc * build: explicit file extensions for imports microsoft/TypeScript#40878 (comment) * chore: add code example * ci: add build step * chore(example): password generation * docs(imports): import-ettiquette * docs(type): `JSDocs` for QueryParameter types
* feat: initial commit * ci: install prettier * ci: setup git hook for lint-staged * style: formatting * ci: add dependabot config * chore: add generateResetPasswordToken() * chore(package): set node version * ci: remove Github Packages upload * chore(users): remove roles * build: npmignore * build: update eslint, gitignore * build: parcel -> tsc * build: explicit file extensions for imports microsoft/TypeScript#40878 (comment) * chore: add code example * ci: add build step * chore(example): password generation * docs(imports): import-ettiquette * docs(type): `JSDocs` for QueryParameter types Co-authored-by: Mufti Azan Farooqi <31279909+azan-n@users.noreply.github.com>
* feat: initial commit * ci: install prettier * ci: setup git hook for lint-staged * style: formatting * ci: add dependabot config * chore: add generateResetPasswordToken() * chore(package): set node version * ci: remove Github Packages upload * chore(users): remove roles * build: npmignore * build: update eslint, gitignore * build: parcel -> tsc * build: explicit file extensions for imports microsoft/TypeScript#40878 (comment) * chore: add code example * ci: add build step * chore(example): password generation * docs(imports): import-ettiquette * docs(type): `JSDocs` for QueryParameter types Co-authored-by: Mufti Azan Farooqi <31279909+azan-n@users.noreply.github.com> Co-authored-by: Mufti Azan Farooqi <31279909+azan-n@users.noreply.github.com>
* feat: initial commit * ci: install prettier * ci: setup git hook for lint-staged * style: formatting * ci: add dependabot config * chore: add generateResetPasswordToken() * chore(package): set node version * ci: remove Github Packages upload * chore(users): remove roles * build: npmignore * build: update eslint, gitignore * build: parcel -> tsc * build: explicit file extensions for imports microsoft/TypeScript#40878 (comment) * chore: add code example * ci: add build step * chore(example): password generation * docs(imports): import-ettiquette * docs(type): `JSDocs` for QueryParameter types * ci: release-please refactor
* feat: initial commit (#2) (#4) * feat: initial commit * ci: install prettier * ci: setup git hook for lint-staged * style: formatting * ci: add dependabot config * chore: add generateResetPasswordToken() * chore(package): set node version * ci: remove Github Packages upload * chore(users): remove roles * build: npmignore * build: update eslint, gitignore * build: parcel -> tsc * build: explicit file extensions for imports microsoft/TypeScript#40878 (comment) * chore: add code example * ci: add build step * chore(example): password generation * docs(imports): import-ettiquette * docs(type): `JSDocs` for QueryParameter types Co-authored-by: Mufti Azan Farooqi <31279909+azan-n@users.noreply.github.com> * ci: release-please fix (#6) * feat: initial commit * ci: install prettier * ci: setup git hook for lint-staged * style: formatting * ci: add dependabot config * chore: add generateResetPasswordToken() * chore(package): set node version * ci: remove Github Packages upload * chore(users): remove roles * build: npmignore * build: update eslint, gitignore * build: parcel -> tsc * build: explicit file extensions for imports microsoft/TypeScript#40878 (comment) * chore: add code example * ci: add build step * chore(example): password generation * docs(imports): import-ettiquette * docs(type): `JSDocs` for QueryParameter types * ci: release-please refactor Co-authored-by: Adnan Kamili <adnan-kamili@users.noreply.github.com>
2023 now, and the flicker of hope for this feature is still alive! |
Here a few pull requests or issues, where i need only file extensions (.js) so I could use them in browser: adobe/css-tools#90 I don't know why an extra option wich I created in my pull request could not be added... |
I know use a js script to add the extension afterwards, see the pull I created here on how to do: wokwi/wokwi-elements#150 |
Just leaving my Would be as simple as to allow using This would make everyone happy: hundreds of people asking for it and TS maintainers because TS principles will not be affected this way (read this comment). Hope TS maintainers will reconsider this soon. (Continues here) |
Cross-posting this comment which explains that this is, very much intentionally, not something we are going to do. |
TypeScript Version: 4.0.3
Search Terms: module es6 es2015 import file extension missing js ts bug 404
Steps to reproduce:
Create a
main.ts
:Create a
dep.ts
:Create a
tsconfig.json
:Then run:
Expected behavior:
The compiler generates JavaScript "which runs anywhere JavaScript runs: In a browser, on Node.JS or in your apps" (according to the TypeScript homepage). For example, it would be valid to create two files as follows;
main.js
:And
dep.js
:(More generally, the expected behavior is that the module specifier in the generated
import
must match the filename chosen for the generated dependency. For example, it would also be valid for the compiler to generate a filedep.xyz
, if it also generatedimport ( foo } from './dep.xyz'
.)Actual behavior:
As above, except that in
main.js
, theimport
URL does not match the filename chosen by the compiler for the generated dependency; it is missing the file extension:When executing
main.js
in the browser, it requests the URL./dep
, which is a 404. This is expected, as the correct relative URL would be./dep.js
.Related Issues: #13422, voluntarily closed by the reporter for unknown reasons
The text was updated successfully, but these errors were encountered: