2

Context

I have these two TypeScript files:

// something.ts

let x = 3;

export default x;
// index.ts

import x from "./something";

console.log(x);

under these tsconfig.json settings:

{
  ...
    "target": "es2016",
  ...
    // "module": "commonjs" /* <- commented out */
}

when I compile this into JavaScript, I get these two files (which is what I expect):

// something.js

let x = 3;
export default x;
// index.js

import x from "./something";
console.log(x);

Now, I've linked the index.js file from my HTML file like so:

<script src="./dist/index.js"></script>

Problem

When I try opening this up using Live Server, I get this error in the console:

Uncaught SyntaxError: Cannot use import statement outside a module

So, how do I fix this?

What I've tried

  1. Adding back the "module": "commonjs" line in my tsconfig.json file

This results in a different error message appearing in the console:

Uncaught ReferenceError: exports is not defined

As per this SO post, a suggested solution is to comment out the "module": "commonjs" line, which essentially throws my situation into an infinite loop...

  1. Adding "type": "module" to the <script> tag and package.json

From this SO post, a suggested solution is to add "type": "module" to the <script> tag:

<script type="module" src="./dist/index.js"></script>

but now I get this error message:

GET http://127.0.0.1:5501/dist/something net::ERR_ABORTED 404 (Not Found).

In the same post, another suggestion was to add "type": "module" to the package.json file:

{
  "type": "module",
  "dependencies": {
    "typescript": "^4.6.2"
  }
}

and from what I've observed, this doesn't really do anything.

Daryll Ko
  • 23
  • 4

1 Answers1

2

You are really really really close. In ESM (in browser) imports, you must provide the file name extension, so you need to do:

import something from "./something.js";

instead of simply

import something from "./something";

Your other code didn't work because the browser runs on, well... the browser. That means we're not in a NodeJS environment so modules don't work so magically. So, the caveats of ESM imports:

  • You can't import npm packages
  • All imports must be an absolute URL or start with ./ or /
  • The browser doesn't automatically add a file extension; you'll have to specify yourself (e.g. import something from "./something.js"; instead of import something from "./something";)
  • You must specify type="module" in your HTML script tag to tell the browser that it's a module you're looking at
  • Please don't use this in production, because...
    • If you have nested imports, the browser can't load them concurrently; they have to be loaded one by one
    • It's not as fast (duh)
    • The feature is quite new so isn't really enhanced that much

The real solution:
Use a bundler instead!!

Why?
A bundler (the most popular is perhaps Webpack) finds all the imports and exports in your code and combines it all into one file.
Why you should use a bundler:

  • Everyone is using it
  • It's the standard
  • It's popular
  • ...
  • It enhances page performance
  • You get other goodies like minification and whatnot
  • You can import (some) npm packages

That doesn't mean we should never use in-browser imports. It's totally fine when you're using it in development, as it's easier to set up anyways. Vite, a popular development framework, takes advantage of this to improve the DX (developer experience) by removing the bundler wait time every time you re-run your code.

But again, don't use this in production.




I suggest you use a bundler since you have Node and TS set up anyways. It'll be easier in the long run...

code
  • 5,690
  • 4
  • 17
  • 39
  • "_All imports must end with .js_": This is not correct: browsers don't discriminate resources by file extension. Rather, each imported script must be served with the correct media type and parse as a valid ES module. – jsejcksn Mar 13 '22 at 06:37
  • @jsejcksn true true. I added that for the sake of simplicity but updated the answer just now for the sake of your comment. – code Mar 13 '22 at 06:52
  • "_the browser can't load them concurrently_": This is also not true. See these posts: [ESM and HTTP/2](https://betterprogramming.pub/2020-004-the-rollout-of-modules-is-complete-d25f04870284), [V8: Modules](https://v8.dev/features/modules#defer). (ESM dependencies are loaded concurrently). – jsejcksn Mar 13 '22 at 07:02