16

It seems import x = require('x') is an invalid syntax in es6, and there is no clear explanation in typescript documentation.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
爱国者
  • 4,298
  • 9
  • 47
  • 66

2 Answers2

36

Q1: import … = require(…) versus const … = require(…)

At runtime (or once the code is compiled), there is no difference between the two syntaxes, the first one is converted to the second one.

With import:

import x = require('x')

This syntax is specific to TypeScript. The constant x is of type given by some typing defined in the imported package or in a package @types/x.

With const:

const x = require('x')

This is a valid syntax in JavaScript and of course in TypeScript. In TypeScript, the constant x is of type any.

Q2: import … from … versus import … = require(…)

How about difference between import x from 'x' and import x = require('x')

The syntax import … from … is from the ES6 standard. I suggest to read this introduction to ES6 modules and how to import and export them.

But, in short, the syntax import x from 'x' is equivalent to:

import x = require('x').default

(Notice the .default member.)

How to convert import … = require(…) to the ES6 syntax

The ES6 standard states that all exported members can be imported into a single "namespace object module".

Then the closest standard syntax of import x = require('x') is:

import * as x from 'x'

This syntax currently works well with the TypeScript transpilation because the code is converted to a const … = require(…).

However: This syntax should be used only in the context defined by the standard. Because, when your code will use a native version of ES6 modules, you won't be able to import a function or a class that way.

Paleo
  • 21,831
  • 4
  • 65
  • 76
4

The require() function doesn't exist in TypeScript. ECMAScript's module system uses import and export keywords. The require, module.exports, and exports keywords exist in the CommonJS module system used by Node.js.

So when you type const x = require('x'), TypeScript is going to complain that it doesn't know what require is. You need to install @types/node package to install type definitions for the CommonJS module system in order to work with it from the TypeScript.

├── src/
|   ├── a.ts
|   └── x.js
└── dist/
    ├── a.js
    └── x.js

Let's imagine you have a.ts and x.js as source files. The a.ts file imports x.js file. Both these files will be compiled to .js files that will run on Node. So let's understand how these will look like when compiled to JavaScript.

// dist/x.js
exports = module.exports = function() { return 'MAIN'; }
exports.custom = function() { return 'CUSTOM'; }

// dist/a.js
const x = require( 'x.js' );
console.log( x() ); // 'MAIN'
console.log( x.custom() ); // 'CUSTOM' 

The x.js sets exports and module.exports to a function that returns MAIN when called. Since a function is also an object in JavaScript, we can assign some properties to it. The custom property is a function that returns CUSTOM when called.

// src/x.js
exports = module.exports = function() { return 'MAIN'; }
exports.custom = function() { return 'CUSTOM'; }

The src/x.js is already a .js file with the CommonJS module syntax. We can import it inside a TypeScript file by using import syntax. We need to set the allowJs property of tsconfig.json to true or use --allowJs flag while compiling the project.

Approach One

// src/a.ts
import x from './x';
console.log( x() ); // === error ===
console.log( x.custom() ); // === error ===

In this example, import x syntax states that x is the default export, however, x.js does not have a default export since that feature lacks in the CommonJS module system. You can allow this by setting the esModuleInterop option to true in the tsconfig.json. So when you try to compile this program, you are going to get the following compilation error.

a.ts:1:8 - error TS1259: Module '"./x"' can only be default-imported using the 'esModuleInterop' flag

1 import x from './src/q';
         ~

  src/q.js:1:11
    1 exports = module.exports = function() { return "MAIN"; }
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    This module is declared with using 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.

Approach Two

// src/a.ts
import * as x from './x';
console.log( x() ); // `MAIN`
console.log( x.custom() ); // `CUSTOM`

In this example, import * as x syntax states that all export members will be stored in the x object. Hence, x itself doesn't represent anything, it is just a container that holds all the exports such as x.custom. However, TypeScript can inter module.exports as x and therefore this program works just fine. But according to ECMAScript specifications, x should not be callable or constructible (using new). So this isn't semantically right.

Approach Three

TypeScript provides import = require() and export = syntax to work with such situations. This syntax is limited to TypeScript only and it can be only used when the module property is set to CommonJS in the tsconfig.json.

The export = syntax represents a single object that will be exported from the module. By default, a JavaScript module with an export in the form of module.exports automatically obtain export = type.

But you can use exports = function(){ ... } syntax in a TypeScript or JavaScript file. The TypeScript compiler will convert this syntax into module.exports syntax in the compiled JavaScript code.

// src/a.ts
import x = require( './x' );
console.log( x() ); // `MAIN`
console.log( x.custom() ); // `CUSTOM`

When a module has exports = type, the ideal way to import it using import = require() syntax.


I would personally prefer to use import x from syntax since we can closely relate it with a CommonJS module and also, we compile the code to ECMAScript and CommonJS module system using the same syntax. By setting esModuleInterop option to true in the tsconfig.json file, the TypeScript compiler emits appropriate helper functions in the compiled JavaScript code to add the default export feature to a CommonJS module.

Community
  • 1
  • 1
Uday Hiwarale
  • 4,028
  • 6
  • 45
  • 48