Skip to content
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

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

Closed
pauldraper opened this issue Sep 8, 2019 · 6 comments
Closed

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

pauldraper opened this issue Sep 8, 2019 · 6 comments
Assignees
Labels
Rescheduled This issue was previously scheduled to an earlier milestone Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@pauldraper
Copy link

pauldraper commented Sep 8, 2019

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.

@0kku
Copy link

0kku commented Sep 8, 2019

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.

@pauldraper
Copy link
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.

@Jack-Works
Copy link
Contributor

#35148

@n-romaniv
Copy link

@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?

@Jack-Works
Copy link
Contributor

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

@RyanCavanaugh RyanCavanaugh added the Rescheduled This issue was previously scheduled to an earlier milestone label Aug 31, 2020
@RyanCavanaugh RyanCavanaugh removed this from the TypeScript 4.3.1 milestone Jun 18, 2021
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 4.4.1 (RC) milestone Jun 18, 2021
@rbuckton
Copy link
Member

rbuckton commented Jul 8, 2021

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

@rbuckton rbuckton closed this as completed Jul 8, 2021
@rbuckton rbuckton added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Needs Investigation This issue needs a team member to investigate its status. labels Jul 8, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Rescheduled This issue was previously scheduled to an earlier milestone Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants