import m = require("m") vs import * as m from "m"

Image by Varsha Y S (Own work) [CC BY-SA 4.0 (https://creativecommons.org/licenses/by-sa/4.0)], via Wikimedia Commons

If you think you understand quantum mechanics, you don't understand quantum mechanics. ~ Probably Richard Feynman

We can say the same about ECMAScript Modules. If anything below does not seem quite right, please let me know @shaunluttin on Twitter.

Importing Modern Modules

Before we compare the import = and import * syntax, lets look at two ways to import modern ECMAScript modules.

import m from "module"

import { m } from "module"

  • Recommended.
  • This is a named/destructured import.
  • Use it to import a named export.

Import of CommonJS/AMD Modules

Though the modern imports are part of ECMAScript 6 Modules' (ESM) specification, neither NodeJS nor major browsers support the ESM loader yet. NodeJS marks ESM as experimental and has a proposal to make ESM interoperate with its existing CommonJS module loader. Major browsers are starting to support ESM. That means we will eventually have ESM as a common module loader API for the server and the browser. In the meantime, we must use Babel or TypeScript to transpile ECMAScript Modules down to CommonJS, AMD, or some other module syntax. Importantly: the emerging ECMA Modules' loader needs to be compatible with the NodeJS/CommonJS module loader. That need for interop is why we have the following two (plus one) import styles:

import m = require("module")

  • Recommended for importing CommonJS modules.
  • The TypeScript Handbook says "When importing a module using export =, TypeScript-specific import module = require("module") must be used to import the module." (And TypeScript is just modern ECMAScript with types.)

import * as m from "module"

  • Bad style. Bad interop. Avoid.
  • Style: AirBnb says "Do not use use wildcard imports. Why? This encourages a single default export."
  • Interop: There is a long conversation about CommonJS interop in this GitHub Issue. The TL;DR; is that ESM needs to remain compatible with CommonJS, and import * as ... might emit non-callable namespace records, which would break both primitive exports (export = 10) and function/constructor exports (export = () => {});

const m = require("module")

  • Legacy. Avoid. This breaks static analysis.

Conclusion

Use import = only to consume legacy modules; avoid import ... as entirely, and approach ECMAScript modules with intellectual humility.