Skip to content

module: esnext should append .js to imports #33588

Description

TypeScript Version: 3.4.0-dev.201xxxxx

Search Terms:
modules

Code
having a local-lib.ts

index.ts

import X from 'local-lib'

should compile with "module": "esnext" to (probably in combination with new extension rewriting flags):

import X from 'local-lib.js'

Expected behavior:
.js should be appended to imports of local es modules
(this is required in node and browser modules support)

Actual behavior:
no .js
Playground Link:

Related Issues:

Activity

  1. RyanCavanaugh commented on Sep 25, 2019

    @RyanCavanaugh
    Member

    TypeScript 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.

  2. amitport commented on Sep 26, 2019

    @amitport
    Author

    Ryan Cavanaugh (@RyanCavanaugh) hi give me a chance before closing...

    1. typescript code can have different compilation targets. target + module in tsconfig should determine and act differently when I'm using:
      1.1. commonjs don't touch extensions
      1.2 nodejs es6 module without type: module meaning it should have .mjs extensions
      1.3 browser es6 and nodejs type: module package meaning it should have .js extensions
    2. I may actually compile in-place. Having hard-coded .js extension in typescript files will mean I'm importing previously generated '.js' files and those will be analysed instead of the imported typescript files.
  3. RyanCavanaugh commented on Sep 26, 2019

    @RyanCavanaugh
    Member

    We 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.

  4. amitport commented on Oct 1, 2019

    @amitport
    Author

    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's target (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 .js extension in import statement will fail since the js file 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.

  5. pvarga-dni commented on Jan 28, 2021

    @pvarga-dni

    For 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 .js for you neither will it change .ts to .js.

  6. amitport commented on Jan 29, 2021

    @amitport
    Author

    pvarga-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)

  7. timdp commented on Jun 29, 2022

    @timdp

    I 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 .ts will get turned into .js by the compiler.

    Furthermore, as outlined above, what it I wanted to output .mjs files?

    Finally, bundlers are a thing. When bundling, the module .js file is never actually emitted. The mental gymnastics to understand that there would have been a .js but 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 .ts module from the source and rewriting the path to .js at 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.

  8. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions