Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unclear: How to import fromPairs from 'lodash/frompairs' #15426

Closed
bcherny opened this issue Apr 28, 2017 · 9 comments
Closed

Unclear: How to import fromPairs from 'lodash/frompairs' #15426

bcherny opened this issue Apr 28, 2017 · 9 comments
Labels
Duplicate An existing issue was already created

Comments

@bcherny
Copy link

bcherny commented Apr 28, 2017

I have been unable to find a clear explanation of what to do in this case:

  • I want to consume a CommonJS module with a single default export
  • I want to compile to ES2015 modules via TSC (I am not using Browserify or System)

Concretely:

// npm i lodash.frompairs @types/lodash.frompairs
import fromPairs = require('lodash.frompairs')

I am trying to create 2 builds of my library: 1 that exposes a CommonJS export, 1 that exposes an ES2015 export. Concretely:

  1. tsc -m commonjs -t es5 Succeeds
  2. tsc -m es2015 -t es2015 Fails with error TS1202: Import assignment cannot be used when targeting ECMAScript 2015 modules

Do I need to introduce System/Browserify into my build, or is it possible to get this to work correctly with TSC?

The handbook's advice is:

When importing a module using export =, TypeScript-specific import module = require("module") must be used to import the module.

Relevant issues:

My full tsconfig.json:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": false,
    "declaration": false,
    "forceConsistentCasingInFileNames": true,
    "jsx": "react",
    "lib": [
      "dom",
      "es2015",
      "es2016.array.include"
    ],
    "module": "commonjs",
    "moduleResolution": "node",
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "preserveConstEnums": true,
    "pretty": true,
    "sourceMap": true,
    "strictNullChecks": true,
    "target": "es6"
  },
  "files": [
    "index.tsx",
    "test.tsx"
  ]
}
@mhegazy
Copy link
Contributor

mhegazy commented Apr 29, 2017

lodash.frompairs is a commonjs module, it exports a function as the module object, i.e. equivalent to export = function frompairs(...) in TypeScript.

TypeScript implementation for ES6 modules adheres to the spec, in the sense that import d from "mod" means get the member called "default" on the module object, and add a local name binding for it in this file and call it d. The generated code does that as well.

It is important to note that the ES6 spec does not describe interop with CommonJS modules, as this is outside the scope of what the committee does.

Also as per the ES6 spec, If you want to get the module object then use import * as m from "mod". The ES6 spec also stipulates that a module object can not be callable. so import * as m from "mod"; m(); is an error in ES6 (TypeScript allows you to do that today, but that is a bug that we have not fixed).

Obviously this does not work in the case of CommonJS modules that override the whole module object. our recommendation here is to use the pre-ES6 import ... = require(..) syntax as that matches the CommonJS/AMD semantics accurately. The down side, is that you can not target ES6 as a module output.

When TS implemented supported for ES6 modules, CommonJS interop story was not clear. i think we now have better understanding of what NodeJS will be doing moving forward and we should be able to allow importing CommonJS modules as default imports using import d from "mod" ES6 import form.

@mhegazy
Copy link
Contributor

mhegazy commented Apr 29, 2017

To calrify my erlier commment, Today an import of the form:

import d from "foo";
console.log(d);

today emits to:

var foo = require("foo");
console.log(foo.default);

under the earlier proposal it would emit instead:

var foo = require("foo"), foo_1 = foo && foo.__esModule ? foo : { default: foo };
console.log(foo_1.default);

@bcherny
Copy link
Author

bcherny commented Apr 30, 2017

@mhegazy Thank you for the explanation - it's very helpful.

we should be able to allow importing CommonJS modules as default imports using import d from "mod" ES6 import form.

var foo = require("foo"), foo_1 = foo && foo.__esModule ? foo : { default: foo };
console.log(foo_1.default);

If I understand right, are you saying that you guys are planning to change emitted output for imported CommonJS modules to the above? If yes, is there an issue tracking it that I can follow?

@mhegazy
Copy link
Contributor

mhegazy commented May 1, 2017

@DanielRosenwasser is working on one. we will be discussing it in the language design meeting this Friday. so stay tuned

@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label May 24, 2017
@AlexGalays
Copy link

google closure also doesn't like these "fake" commonJS modules :p it never reads the default property.

tlvince added a commit to tlvince/ngcomponent that referenced this issue Jul 12, 2017
I tried a few permutations and eventually the TypeScript-specific
`import some = require('lodash.some')`, which works fine for the ES5
build, but not ES2015 per:

microsoft/TypeScript#15426.

Falling back to a standard `require` was the only way I could make this
work (thus far).

However, this means `index.es2015.js` is no longer an ES module. Lmk if
there's a better way of this.

Closes coatue-oss#36.
@ngbrown
Copy link

ngbrown commented Jul 14, 2017

Any progress with interoperating with CommonJS modules?

@chompy18
Copy link

chompy18 commented Aug 2, 2017

Any progress with importing CommonJS modules as es6 imports?
Currently I am using:
import difference = require("lodash/difference");

@mhegazy
Copy link
Contributor

mhegazy commented Aug 17, 2017

#16093 tracks this work.

@mhegazy mhegazy added Duplicate An existing issue was already created and removed Needs Investigation This issue needs a team member to investigate its status. labels Aug 17, 2017
@mhegazy
Copy link
Contributor

mhegazy commented Sep 5, 2017

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@mhegazy mhegazy closed this as completed Sep 5, 2017
@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

6 participants