Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between `import x = require('x')` and `const x = require('x')` in typescript

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

like image 725
爱国者 Avatar asked Sep 27 '18 10:09

爱国者


People also ask

What is the difference between import and require in TypeScript?

Key Differences Between Require and ImportRequire is Nonlexical and Import is Lexical. Requires to stay where they have put the file, and imports get sorted to the top of the file. Import is always run at the very beginning of the file and can't be run conditionally.

What is the difference between import and require?

Require is Non-lexical, it stays where they have put the file. Import is lexical, it gets sorted to the top of the file. It can be called at any time and place in the program. It can't be called conditionally, it always run in the beginning of the file.

Should I use import or require for Express?

The major difference between require and import , is that require will automatically scan node_modules to find modules, but import , which comes from ES6, won't. Most people use babel to compile import and export , which makes import act the same as require .

Is it OK to mix require and import?

Yes, this is acceptable in TypeScript.


2 Answers

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.

like image 152
Paleo Avatar answered Oct 28 '22 22:10

Paleo


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.

like image 36
Uday Hiwarale Avatar answered Oct 28 '22 23:10

Uday Hiwarale