TypeScript: Importing defaults with es6 syntax doesn't work

Syntax: import name from "module-name"; doesnt work in my project with Typescript 1.6. The imported variable is undefined.

Example:

import createLogger from "redux-logger"; //createLogger is undefined

This syntax works

import createLogger = require("redux-logger");

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 3
  • Comments: 22 (7 by maintainers)

Most upvoted comments

You need to write it as a namespace import (as opposed to a default import):

import * as createLogger from "redux-logger";

Ok, I think I see what is going on.

It appears the “redux-logger” module was transpiled with Babel. When Babel transpiles a module whose only export is an export default it injects a module.exports = exports["default"]; into the generated code, causing the exported object to become the function itself (instead of a module object that has the function as the default property). When paired with the _interopRequireDefault magic that Babel generates for imports the net effect is that a fake module object with a default property is created and the function can now be accessed as _reduxLogger2.default.

TypeScript doesn’t do any of this magic (see here for more details). In order for TypeScript to consume the module you need to change the redux-logger.d.ts declaration file to actually reflect what is going on:

/// <reference path="../redux/redux.d.ts" />

declare module 'redux-logger' {

  function createLogger(options?: createLogger.ReduxLoggerOptions): Redux.Middleware;

  namespace createLogger {
    interface ReduxLoggerOptions {
      actionTransformer?: (action: any) => any;
      collapsed?: boolean;
      duration?: boolean;
      level?: string;
      logger?: any;
      predicate?: (getState: Function, action: any) => boolean;
      timestamp?: boolean;
      transformer?: (state:any) => any;
    }
  }

  export = createLogger;
}

Once you do that you should be able to import the module with a namespace import:

import * as createLogger from "redux-logger";

or with the equivalent:

import createLogger = require("redux-logger");

@aventurella Thanks. The complexities of ES6 module downlevel transpilation and interop is truly the gift that keeps on giving!

@ahejlsberg I’m using commonjs module loader. I compared my compiled code with babel code and here are two versions:

Typescript:

var redux_logger_1 = __webpack_require__(678);

var test = redux_logger_1.default;

Babel:

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

var _reduxLogger = require("redux-logger");

var _reduxLogger2 = _interopRequireDefault(_reduxLogger);

and in the runtime redux_logger_1 is a function and redux_logger_1.default is undefined. Babel code works because the condition inside function is true so it returns obj


To sum up what I’ve said earlier: My goal is to import a node_module which exports one default function. In pure es6 javascript I would do that with import module from "module". In typescript I tried to do it in the same way with the same syntax but my module variable was undefined during runtime. What is more weird for me this syntax was working when I was trying to link one .ts file to another .ts file.

I discovered that Babel compiles default export to another syntax that Typescript and Typescript can’t handle babel output. For example I have the following code: export default function defaultFunction() {} In babel it will be

exports.__esModule = true;
exports['default'] = defaultFunction;
module.exports = exports['default'];

In typescript

Object.defineProperty(exports, "__esModule", { value: true });
exports.default = defaultFunction

As I said the first output is incompatible with Typescript. The imports that don’t work: import defaultFunction from "module" - defaultFunction is undefined import { defaultFunction } from "module" - It doesn’t compile if the function is declared as default in definitions import * as defaultFunction from "module" - calling defaultFunction() causes compiler error error TS2349: Cannot invoke an expression whose type lacks a call signature. import defaultFunction = require("module"); - calling defaultFunction() causes compiler error error TS2349: Cannot invoke an expression whose type lacks a call signature. const defaultFunction = require("module"); - works without typescript support but you can add casting to get some intellisense

With the second output I can just do import defaultFunction from "module"

Thanks @aluanhaddad. I just submitted a new pull request to fix it.

@wy193777 the named export is the problem. I believe you want

+export = cytoscape;
+export as namespace cytoscape;
- export function cytoscape(options?: cytoscape.CytoscapeOptions): cytoscape.Core;
+ function cytoscape(options?: cytoscape.CytoscapeOptions): cytoscape.Core;
- export function cytoscape(extensionName: string, foo: string, bar: any): cytoscape.Core;
+ function cytoscape(extensionName: string, foo: string, bar: any): cytoscape.Core;
- export namespace cytoscape {
+ namespace cytoscape {

based on the package’s source code

Then you would import it via either

import cytoscape = require('cytoscape');

or

import cytoscape from 'cytoscape'; // --allowSyntheticDefaultImports true

or referencing the global when not using modules, depending on your environment.

The only way I have been able to “address” it is by understanding how the target 3rd party module has been exported. And you adjust things accordingly once you see how it’s been exported by whatever transpiled it. Once you know that, if you are adding typings you just need to be sure you add them to describe the situation you observe in the 3rd party module.

I know this was closed a while ago, I just wanted to pop in an give a HUGE THANK YOU to @ahejlsberg for that explanation and remedy remove module.exports = exports["default"];.

You just made some things click for me with that. I literally wasted probably 4 hours trying to understand why my type definition, using export default was returning undefined. Then I stumbled upon export = Thing and was further scratching my head. Why on earth does import * as Thing from 'thing' work with export = Thing in my definition and then when I export default Thing I get undefined with import Thing from 'thing'. Babel… that’s why. hahahahaha oh man I am so glad I understand this now.

I would offer that it’s probably worth adding to the documentation here: https://typescript.codeplex.com/wikipage?title=Writing Definition (.d.ts) Files

As a special note for type definitions for Babel generated code.