28

I'm trying to work with TypeScript in my node project, but I have some issues with that.

This is my index.ts file:

import express from 'express';

const app = express();

I'm running:

tsc --module commonsjs -d index.ts

My output is index.js:

var express_1 = require('express');
var app = express_1["default"]();

Where did this ["default"] came from? It is making my code not to run properly:

var app = express_1["default"]();
                              ^

TypeError: express_1.default is not a function

As far as I understand, I should have got the code without the "default" brackets and it would have worked fine - I tried removing the brackets and it worked.

What am I missing here?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shikloshi
  • 3,761
  • 7
  • 32
  • 58
  • Module should be `commonjs` and not `commonsjs`. That may be causing you a problem. – unflores Oct 22 '18 at 15:15
  • 2
    Consider using esModuleInterop: true compiler option (set it on your tsconfig). This allows you to do `import express from 'express'` as you would expect – Biel Simon Aug 30 '19 at 23:19
  • instead of `import express from 'express'` you could also try `import * as express from 'express'` - which one will work usually depends on _esModuleInterop_ setting in tsconfig.json – Jindrich Vavruska Sep 26 '21 at 09:18

5 Answers5

39

I solved this by adding the following to tsconfig.json:

{
  "compilerOptions": {
    ... 
    "module": "commonjs",
    "esModuleInterop": true,
    ...
  }
}

The esModuleInterop flag is described as: "Emit __importStar and __importDefault helpers for runtime babel ecosystem compatibility and enable --allowSyntheticDefaultImports for typesystem compatibility."

https://www.typescriptlang.org/docs/handbook/compiler-options.html

Liam
  • 697
  • 8
  • 16
  • 1
    This solved my problem! Could've known [ts-node-dev](https://www.npmjs.com/package/ts-node-dev) is using TypeScript's configuration, so I added a `tsconfig.json` file. Thanks! – Derk Jan Speelman Aug 20 '19 at 13:38
  • 3
    This should be the accepted answer, as it allows to simply use `import express from 'express'` – Foxhoundn Jan 22 '20 at 22:26
  • I agree this is the correct strategy with newer versions of typescript. This option wasn't made available until typescript 2.7, which came out in 2018. – dvlsg Jan 27 '20 at 19:23
37

The safest solution would be:

import express = require('express');

This transpiles to:

var express = require('express');

The official documentation for import require declarations can be found here.

I believe TypeScript expects an export named "default" to function as your code above, judging from the final paragraph here.


Side note: It looks like TypeScript's newest version (typescript@1.8.0-dev.20151229 at the time of writing) will throw a warning on a compile attempt which would attempt to use a missing default:

index.ts(1,8): error TS1192: Module '"express"' has no default export.

Side note 2: An example from Microsoft using the import * as express from 'express'; syntax can be found here. When targeting a module of commonjs (as they are in this example), this will also transpile to var express = require('express');.


If you have at least TypeScript 2.7 and are targeting CommonJS, you can use esModuleInterop, as well.

From the link:

To give users the same runtime behavior as Babel or Webpack, TypeScript provides a new --esModuleInterop flag when emitting to legacy module formats.

Under the new --esModuleInterop flag, these callable CommonJS modules must be imported as default imports like so:

import express from "express";

let app = express();

We strongly suggest that Node.js users leverage this flag with a module target of CommonJS for libraries like Express.js, which export a callable/constructable module.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
dvlsg
  • 5,378
  • 2
  • 29
  • 34
  • 1
    `import *` is the wrong way to import legacy modules. See http://stackoverflow.com/a/29598404/252087. – C Snover Dec 30 '15 at 04:15
  • 3
    Consider using esModuleInterop: true compiler option (set it on your tsconfig). This allows you to do `import express from 'express'` as you would expect – Biel Simon Aug 30 '19 at 23:19
7

If you are trying to use the default export of a non-ES6 module like Express.js, you need to use the legacy import syntax import express = require('express').

In ES6 modules, there is no default value export like the module.exports of Node.js modules or the return of AMD modules; the default export of an ES6 module is just the default key. This is why, when you use an ES6 default import as you are attempting to do, TypeScript generates JavaScript with an access to the default property.

More information about this is available at New es6 syntax for importing commonjs / amd modules i.e. `import foo = require('foo')`.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
C Snover
  • 17,908
  • 5
  • 29
  • 39
  • Thanks! I had some research on the subject thanks to you. – Shikloshi Dec 30 '15 at 12:23
  • I don’t understand why you accepted the other answer as the correct answer. It’s the wrong answer. – C Snover Dec 30 '15 at 17:35
  • If it is truly the wrong answer for the current version of Typescript, then I suggest you have Microsoft edit their examples. – dvlsg Dec 30 '15 at 17:50
  • @dvlsg That Microsoft example is *not* the same as what the OP is doing! The example uses only the `static` property from `express`, it does *not* try to use the default export (because it couldn’t, because it’s wrong and won’t work). In such a case, `import *` is fine, because it makes the imported `express` identifier a blank List, then adds all exported properties of the express module onto that List, per the ES specification. It *not* correct for using the default export. – C Snover Dec 30 '15 at 18:17
  • As far as I can tell, the `const app = express();` in the original question is exactly what is being done in the Microsoft [example](https://github.com/Microsoft/TypeScriptSamples/blob/f206e7ca3e0f605ee5ec8f9b30894881dbe4c462/imageboard/app.ts#L11) (minus the const, of course). I agree that `require` will work in more scenarios, however, so I will edit accordingly. – dvlsg Dec 30 '15 at 18:23
  • Sorry, I missed that line. That really should not be working, since the ES spec is explicit that the value of a `* as` import is [a Namespace Object](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-module-namespace-objects), not any callable item, and implementation of ES6 modules syntax was [supposed to disallow it](http://stackoverflow.com/a/29598404/252087). So I’m trying to track down when and how the semantics of `* as` were changed to not fail in this case. – C Snover Dec 30 '15 at 19:00
  • Perhaps it allows it because my test environment and their example were both aiming for `commonjs` (for better or worse or unintentionally)? Targeting ES6 with no module defined resulted in error TS1202 (import assignment cannot be used), but does attempt to transpile. ES6/ES5 with commonjs as the module both transpiled to `var express = require('express');`. Targeting ES5 with no module defined doesn't even attempt to transpile, and suggests setting a module. – dvlsg Dec 30 '15 at 20:11
5

If you still want to use the import keyword then use it like:

import express from "express"; 
// If the above is not supported by your project environment then follow as below
import * as express from "express";

In file tsconfig.json

{
  "compilerOptions": {
    ...   
    "module": "commonjs"
    ...
  }
}

Thanks to Josh Dando.

WasiF
  • 26,101
  • 16
  • 120
  • 128
-1

Another solution that could work for you is do it like this:

import * as express from express;
const app = express();

It should work in the same way.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131