Skip to content

TypeScript ES modules incompatible with browser and Node.js #33306

Description

Typescript is incompatible with ECMAScript modules, as implemented in browsers and Node.js (the two most common ES runtimes).

TypeScript Version: 3.6.2 (Node.js 12.7.0)

Search Terms: ES2015 module, browser, Node.js

Code

https://github.com/pauldraper/ts-es-import

yarn install
yarn run build

A.

yarn run run

B.

yarn run serve

Browse to http://localhost:8080 with ES2015-compatible browser (Chrome, Edge, Firefox, Safari).

Expected behavior:

A.

"Hello world" is printed in the console.

B.

"Hello world" is printed.

Actual behavior:

A.

GET http://localhost:8080/out/lib net::ERR_ABORTED 404 (Not Found)

B.

Error: Cannot find module '/home/paul/dev/pauldraper/typescript-nodejs/out/lib' imported from /home/paul/dev/pauldraper/typescript-nodejs/out/main.js

Playground Link:

Related Issues:


The issue is that while Node's CommonJS modules use a list of implicit file extensions, Node and browsers require the extension.

And neither Typescript's classic nor node resolution strategies permit a file extension. This choice allows tsc to not touch the import specifiers, as the .ts/.d.ts/.tsx/.js/.jsx/.json relationship stays the same before and after compilation.

Solutions:

  1. Nothing. End-user maintains a post-process that either modifies the outputted import specifiers, or modifies the outputted file locations.

  2. TypeScript adds a new --module value ESBrowser, which transforms the output import specifiers to have .js extension.

  3. TypeScript adds a new option --outputExtension which when false or 'remove', strips the extension from the outputted file.

Activity

  1. 0kku commented on Sep 8, 2019

    @0kku

    TS allows .js in module imports and that works correctly. Even when you're importing .ts or other file types that won't have .js extension until after compilation. You can't import .json.

  2. pauldraper commented on Sep 9, 2019

    @pauldraper
    Author

    Oh interesting. Using .js imports does fix it.

    This appears to be undocumented?

    The closest the documentation says is

    To accomplish this, TypeScript overlays the TypeScript source file extensions (.ts, .tsx, and .d.ts) over Node’s resolution logic.

  3. Jack-Works commented on Nov 17, 2019

    @Jack-Works
    Contributor
  4. n-romaniv commented on Jan 12, 2020

    @n-romaniv

    Jack Works (@Jack-Works), sorry, but wouldn't this require more than just file extension? For example, according to the Node.js documentation, there is nothing special about index.js when using ES modules, so using Node module resolution might produce code that will fail in runtime. Or am I missing something?

  5. Jack-Works commented on Jan 12, 2020

    @Jack-Works
    Contributor

    Jack Works (@Jack-Works), sorry, but wouldn't this require more than just file extension? For example, according to the Node.js documentation, there is nothing special about index.js when using ES modules, so using Node module resolution might produce code that will fail in runtime. Or am I missing something?

    Yeah my PR doesn't include solution for Node. It's for (relative files of) browser and deno.

  6. 4 remaining items

  7. rbuckton commented on Jul 8, 2021

    @rbuckton
    Contributor

    TypeScript uses the literal import you wrote, and will attempt to resolve a .js to a .ts at compile time if one is available:

    // a.ts
    import { x } from "./b.js";
    // b.ts
    export const x = 1;

    We have previously discussed this in our design meetings and we are not likely to change this behavior or add an option to automatically add extensions to imports or strip out extensions.

    You can also configure VS Code to add an implicit file extension for automatic imports using the typescript.preferences.importModuleSpecifierEnding setting:

    image

  8. added
    Working as IntendedThe behavior described is the intended behavior; this is not a bug
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    on Jul 8, 2021
  9. 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

Labels

RescheduledThis issue was previously scheduled to an earlier milestoneWorking as IntendedThe behavior described is the intended behavior; this is not a bug

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions