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

Write a FAQ entry for export = vs export default / import variants #7185

Closed
RyanCavanaugh opened this issue Feb 22, 2016 · 14 comments
Closed
Labels
Docs The issue relates to how you learn TypeScript

Comments

@RyanCavanaugh
Copy link
Member

People don't understand the difference between these things:

export default class Foo {}
/* or */
class Foo {}
export = Foo;
/* or */
export class Foo { }

People also don't understand the difference between these things:

import x = require('y');
import x from 'y';
import { x } from 'y'
import * as x from 'y';

We need to write a comprehensive answer that explains which import statements correctly acquire which exports, and also describe auto-lifting behavior present in some loaders.

@RyanCavanaugh RyanCavanaugh added the Docs The issue relates to how you learn TypeScript label Feb 22, 2016
@billti
Copy link
Member

billti commented Feb 23, 2016

I've actually been trying to write this up today as part of fixing up the module bugs assigned to me. I could extract out the general info from the internal info as the basis.

Maybe I'll regret this... but feel free to assign to me if someone is not already working on it.

@mhegazy
Copy link
Contributor

mhegazy commented Feb 23, 2016

We should put any additional information for this in the handbook module page: https://github.com/Microsoft/TypeScript-Handbook/blob/master/pages/Modules.md

we can also add a link to that page in the FAQ page.

@billti
Copy link
Member

billti commented Feb 23, 2016

Looks good! A couple of things that would be really useful additions:

  • Some of the config/path work for modules that @vladima did. It'd be useful to add that here (when it ships I guess).
  • Clarify interop (e.g. working with existing modules written in a CommonJS style - for example, I'm pretty sure this statement in there is wrong: "When importing a module using export =, TypeScript-specific import let = require("module") must be used to import the module.").
  • Are there any consideration for interop with Babel or SystemJS (e.g. the impact of the __esModule property and hoisting of the module.exports to the default property by some loaders, and our allowSyntheticDefaultImports flag).
  • Recently added augmentation for modules.
  • Perhaps some examples of a project configuration that works at runtime (I see more folks struggle here than with getting compilation to work - especially with loaders like SystemJS).

@0815fox
Copy link

0815fox commented Mar 23, 2016

I do not know, who is responsible for the handbook on typescriptlang.org, but the chapter "Going external" should also cover this topic, or at least a hint:
http://www.typescriptlang.org/Handbook#modules-going-external

@madelson
Copy link

I'd add export as namespace Foo to this list.

@boyangwang
Copy link

+1 pls. I dont even know the answer now that finding this issue :/

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Dec 30, 2017

TL;DR: export = or import = require implies the module was written as CommonJS, export default implies it's been authored as an ECMAScript module.

export = and import x = require('./x') corresponds to CommonJS/AMD/etc.'s notion of module.exports:

// ./a.js
module.exports = { a: 10, b: 20 };

// ./b.js
let a = require('./a');

A lot of the time, people will have exactly one thing that they're exporting, like a function:

// ./a.js
module.exports = function foo() { /*...*/ }

// ./b.js
var a = require('./a');
a();

export default is a construct in ES2015 modules that is supposed to give an easy way to say "hey, here's the main thing you import from this module". ES2015 gives it a special syntax.

// a.js
export default function foo() {
    // ...
}

// b.js
import a from './a.js';
a();

But it's just special syntax; really, default is like any member on an ES2015 module record. b.js could have been written like the following:

import * as a from './a.js';
a.default();

import { default as aFujnc } from './a.js'
aFunc();

@brianjenkins94
Copy link

brianjenkins94 commented Sep 15, 2018

export default ... (Default Export)

// calculator.ts                                // compiled.js
// =============                                // ===========
export default class Calculator {               // var Calculator = /** @class */ (function () {
    public add(num1, num2) {                    //     function Calculator() {}
        return num1 + num2;                     //     Calculator.prototype.add = function (num1, num2) {
    }                                           //         return num1 + num2;
}                                               //     };
                                                //     return Calculator;
                                                // }());
                                                // exports["default"] = Calculator;

import ... from "module";

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import Calculator from "./calculator";          // exports.__esModule = true;
                                                // var calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new calculator["default"]();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • A default export can be imported with any name.
  • Functionally equivalent to import * as Calculator from "./calculator"; and then instantiating it using new Calculator.default().

export = ...

// calculator.ts                                // compiled.js
// =============                                // ===========
export = class Calculator {                     // module.exports = /** @class */ (function () {
    public add(num1, num2) {                    //     function Calculator() {}
        return num1 + num2;                     //     Calculator.prototype.add = function (num1, num2) {
    }                                           //         return num1 + num2;
}                                               //     };
                                                //     return Calculator;
                                                // }());

import ... = require("module");

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import Calculator = require("./calculator");    // exports.__esModule = true;
                                                // var Calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new Calculator();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • This syntax is only used when importing a CommonJS module.

export ... (Named Export)

// calculator.ts                                // compiled.js
// =============                                // ===========
export class Calculator {                       // exports.__esModule = true;
    public add(num1, num2) {                    // var Calculator = /** @class */ (function () {
        return num1 + num2;                     //     function Calculator() {}
    }                                           //     Calculator.prototype.add = function (num1, num2) {
}                                               //         return num1 + num2;
                                                //     };
                                                //     return Calculator;
                                                // }());
                                                // exports.Calculator = Calculator;

import { ... } from "module";

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import { Calculator } from "./calculator";      // exports.__esModule = true;
                                                // var calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new calculator.Calculator();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • Named exports are useful to export several values.
  • During the import, you must use the same name of the corresponding object.

@EltonZhong
Copy link

export default ... (Default Export)

// calculator.ts                                // compiled.js
// =============                                // ===========
export default class Calculator {               // var Calculator = /** @class */ (function () {
    public add(num1, num2) {                    //     function Calculator() {}
        return num1 + num2;                     //     Calculator.prototype.add = function (num1, num2) {
    }                                           //         return num1 + num2;
}                                               //     };
                                                //     return Calculator;
                                                // }());
                                                // exports["default"] = Calculator;

import ... from "module";

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import Calculator from "./calculator";          // exports.__esModule = true;
                                                // var calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new calculator["default"]();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • A default export can be imported with any name.
  • Functionally equivalent to import * as Calculator from "./calculator"; and then instantiating it using new Calculator.default().

export = ...

// calculator.ts                                // compiled.js
// =============                                // ===========
export = class Calculator {                     // module.exports = /** @class */ (function () {
    public add(num1, num2) {                    //     function Calculator() {}
        return num1 + num2;                     //     Calculator.prototype.add = function (num1, num2) {
    }                                           //         return num1 + num2;
}                                               //     };
                                                //     return Calculator;
                                                // }());

import ... = require("module");

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import Calculator = require("./calculator");    // exports.__esModule = true;
                                                // var Calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new Calculator();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • This syntax is only used when importing a CommonJS module.

export ... (Named Export)

// calculator.ts                                // compiled.js
// =============                                // ===========
export class Calculator {                       // exports.__esModule = true;
    public add(num1, num2) {                    // var Calculator = /** @class */ (function () {
        return num1 + num2;                     //     function Calculator() {}
    }                                           //     Calculator.prototype.add = function (num1, num2) {
}                                               //         return num1 + num2;
                                                //     };
                                                //     return Calculator;
                                                // }());
                                                // exports.Calculator = Calculator;

import { ... } from "module";

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import { Calculator } from "./calculator";      // exports.__esModule = true;
                                                // var calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new calculator.Calculator();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • Named exports are useful to export several values.
  • During the import, you must use the same name of the corresponding object.
    Very helpful, thank you.

@ShiChenCong
Copy link

export default ... (Default Export)

// calculator.ts                                // compiled.js
// =============                                // ===========
export default class Calculator {               // var Calculator = /** @class */ (function () {
    public add(num1, num2) {                    //     function Calculator() {}
        return num1 + num2;                     //     Calculator.prototype.add = function (num1, num2) {
    }                                           //         return num1 + num2;
}                                               //     };
                                                //     return Calculator;
                                                // }());
                                                // exports["default"] = Calculator;

import ... from "module";

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import Calculator from "./calculator";          // exports.__esModule = true;
                                                // var calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new calculator["default"]();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • A default export can be imported with any name.
  • Functionally equivalent to import * as Calculator from "./calculator"; and then instantiating it using new Calculator.default().

export = ...

// calculator.ts                                // compiled.js
// =============                                // ===========
export = class Calculator {                     // module.exports = /** @class */ (function () {
    public add(num1, num2) {                    //     function Calculator() {}
        return num1 + num2;                     //     Calculator.prototype.add = function (num1, num2) {
    }                                           //         return num1 + num2;
}                                               //     };
                                                //     return Calculator;
                                                // }());

import ... = require("module");

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import Calculator = require("./calculator");    // exports.__esModule = true;
                                                // var Calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new Calculator();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • This syntax is only used when importing a CommonJS module.

export ... (Named Export)

// calculator.ts                                // compiled.js
// =============                                // ===========
export class Calculator {                       // exports.__esModule = true;
    public add(num1, num2) {                    // var Calculator = /** @class */ (function () {
        return num1 + num2;                     //     function Calculator() {}
    }                                           //     Calculator.prototype.add = function (num1, num2) {
}                                               //         return num1 + num2;
                                                //     };
                                                //     return Calculator;
                                                // }());
                                                // exports.Calculator = Calculator;

import { ... } from "module";

// importer.ts                                  // compiled.js
// ===========                                  // ===========
import { Calculator } from "./calculator";      // exports.__esModule = true;
                                                // var calculator = require("./calculator");
let calc = new Calculator();                    // var calc = new calculator.Calculator();
                                                // console.log(calc.add(2, 2));
console.log(calc.add(2, 2));                    //
Notes:
  • Named exports are useful to export several values.
  • During the import, you must use the same name of the corresponding object.
    Very helpful, thank you.

import = require does not work for me

// calculator.ts                                
// =============                              
export class Calculator {                    
    public add(num1, num2) {                    
        return num1 + num2;                 
    }                                          
}                                              
      

import { Calculator } from "./calculator";      
                                              
let calc = new Calculator();                 
                                              
console.log(calc.add(2, 2));                                            

image

@brianjenkins94
Copy link

This is most likely the wrong forum for your issue. I would suggest either Stack Overflow or creating a new issue.

@Falieson
Copy link

Falieson commented Feb 7, 2019

I've been using TS for over a year and I'm still confused about the below. I've thrown it on stackoverflow but I bet it'll be helpful for this documentation as well.

/// file1
export {
  PORT,
  SSL_CRT,
  SSL_KEY,
}
// file2
import * as env from 'file1'
export {
   ...env  // [ts] Identifier expected. [1003] 
}

I resolve this issue with the following but I know its not the same nor what I want.

// file2
import * as env from 'file1'
export default {
   ...env  // [ts] Identifier expected. [1003] 
}

iamakulov added a commit to iamakulov/clean-deep that referenced this issue Jun 15, 2020
The source code uses `module.exports = ...`, not `exports.default = ...`:

https://github.com/nunofgs/clean-deep/blob/9d8313db868b239b990f9db352215844ad7ae65a/src/index.js#L14-L22

Given this, TS definitions should use `export =`, not `export default`. See microsoft/TypeScript#7185 (comment).

This matters when the project is compiled _without_ the `"esModuleInterop": true` flag. In that case, doing

```ts
import cleanDeep from 'clean-deep'

cleanDeep(...)
```

would throw an error like `TypeError: clean_deep_1.default is not a function`.
niedzielski added a commit to niedzielski/web-worker that referenced this issue May 18, 2022
- Add types entry exports[0]. I'm sad TypeScript doesn't seem to be
  using the plain old types field but it isn't.
- Replace index.d.ts' `export default` with `export =`[1]. From what I
  can tell, `module.exports` is always used for all entrypoints so this
  is the correct definition.
- Use the Worker var declaration from lib.dom.d.ts. I think this is fine
  since the old definition already used the Worker interface from the
  same file. This typing is more accurate, easier to read,d less likely
  to become outdated, and less code.

Bug: developit#20 developit#22

[0]: https://devblogs.microsoft.com/typescript/announcing-typescript-4-5-beta/#packagejson-exports-imports-and-self-referencing
[1]: microsoft/TypeScript#7185 (comment)
@juannguyenicd
Copy link

// a.ts

let a = 0;

export default a;

setInterval(() => {
  a++;
}, 1000);

=====================================
// b.ts

export let b = 0;

setInterval(() => {
  b++;
}, 1000);

====================================
// index.ts

import a from './a';
import { b } from './b';

setInterval(() => {
  console.log('a =', a);
  console.log('b =', b);
  console.log('====');
  console.log();
}, 1000);

// tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true,
    "baseUrl": "."
  }
}

Run: ts-node index.ts

Offroaders123 added a commit to Offroaders123/jsmediatags that referenced this issue Feb 22, 2024
Moving this over to the proper was TS does things for CJS, rather than treating it like `export default`, which I was mistakenly doing previously.

microsoft/TypeScript#7185

Listening to Blackwater Park, Blackwater Park.
Offroaders123 added a commit to Offroaders123/jsmediatags that referenced this issue Feb 22, 2024
Moving this over to the proper was TS does things for CJS, rather than treating it like `export default`, which I was mistakenly doing previously.

microsoft/TypeScript#7185

Listening to Blackwater Park, Blackwater Park.
@RyanCavanaugh
Copy link
Member Author

This is now an entire set of module docs 🥰

See https://www.typescriptlang.org/docs/handbook/modules/introduction.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Docs The issue relates to how you learn TypeScript
Projects
None yet
Development

No branches or pull requests

13 participants