423

What is the difference in TypeScript between export and default export?

In all the tutorials, I see people exporting their classes and I cannot compile my code if I don't add the default keyword before exporting.

Also, I couldn't find any trace of the default export keyword in the official TypeScript documentation.

export class MyClass {

  collection = [1,2,3];

}

Does not compile. But:

export default class MyClass {

  collection = [1,2,3];

}

Does.

The error is:

error TS1192: Module '"src/app/MyClass"' has no default export.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
fos.alex
  • 5,317
  • 4
  • 16
  • 18
  • This might help: http://stackoverflow.com/q/32236163/218196 – Felix Kling Oct 23 '15 at 15:33
  • 3
    Some [light reading](https://github.com/Microsoft/TypeScript/issues/2242) on the topic. It might help if you show how you're importing this class, I believe that's where the error is occurring (you probably need to alter the import syntax to fix the error scenario). – Sunil D. Oct 23 '15 at 16:00
  • 11
    "export" and "export default" are not TypeScript at all - they're ES6. – Sensei James Sep 22 '16 at 17:01
  • 1
    [Worth reading, "Avoid Export Default"](https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html) – Diego Osornio Dec 27 '19 at 22:34
  • The basarat "Avoid Export Default" link is now https://basarat.gitbook.io/typescript/main-1/defaultisbad – Matthias Dec 22 '21 at 16:23

4 Answers4

650

Default Export (export default)

// MyClass.ts -- using default export
export default class MyClass { /* ... */ }

The main difference is that you can only have one default export per file and you import it like so:

import MyClass from "./MyClass";

You can give it any name you like. For example this works fine:

import MyClassAlias from "./MyClass";

Named Export (export)

// MyClass.ts -- using named exports
export class MyClass { /* ... */ }
export class MyOtherClass { /* ... */ }

When you use a named export, you can have multiple exports per file and you need to import the exports surrounded in braces:

import { MyClass } from "./MyClass";

Note: Adding the braces will fix the error you're describing in your question and the name specified in the braces needs to match the name of the export.

Or say your file exported multiple classes, then you could import both like so:

import { MyClass, MyOtherClass } from "./MyClass";
// use MyClass and MyOtherClass

Or you could give either of them a different name in this file:

import { MyClass, MyOtherClass as MyOtherClassAlias } from "./MyClass";
// use MyClass and MyOtherClassAlias

Or you could import everything that's exported by using * as:

import * as MyClasses from "./MyClass";
// use MyClasses.MyClass and MyClasses.MyOtherClass here

Which to use?

In ES6, default exports are concise because their use case is more common; however, when I am working on code internal to a project in TypeScript, I prefer to use named exports instead of default exports almost all the time because it works very well with code refactoring. For example, if you default export a class and rename that class, it will only rename the class in that file and not any of the other references in other files. With named exports it will rename the class and all the references to that class in all the other files.

It also plays very nicely with barrel files (files that use namespace exports—export *—to export other files). An example of this is shown in the "example" section of this answer.

Note that my opinion on using named exports even when there is only one export is contrary to the TypeScript Handbook—see the "Red Flags" section. I believe this recommendation only applies when you are creating an API for other people to use and the code is not internal to your project. When I'm designing an API for people to use, I'll use a default export so people can do import myLibraryDefaultExport from "my-library-name";. If you disagree with me about doing this, I would love to hear your reasoning.

That said, find what you prefer! You could use one, the other, or both at the same time.

Additional Points

A default export is actually a named export with the name default, so if the file has a default export then you can also import by doing:

import { default as MyClass } from "./MyClass";

And take note these other ways to import exist: 

import MyDefaultExportedClass, { Class1, Class2 } from "./SomeFile";
import MyDefaultExportedClass, * as Classes from "./SomeFile";
import "./SomeFile"; // runs SomeFile.js without importing any exports
David Sherret
  • 101,669
  • 28
  • 188
  • 178
  • 6
    what happened to `import myAlias = require("./PathToFile")` and having `export = IInterfaceOrClass` in the file? Is that old fashioned now? – BenCr Apr 19 '16 at 16:02
  • @BenCr yes, this is the [new es6 way](https://developer.mozilla.org/en/docs/web/javascript/reference/statements/import) – David Sherret Apr 19 '16 at 16:13
  • Why don't you give an example of a 'Named Export'? – Stato Machino Mar 14 '17 at 21:01
  • aws-sdk/clients/sns doesn't have default exports and when accessing sns using import sns from '/sns' i get no export but the import myAlias = require("./PathToFile") works . can i do something to change it import sns from '/sns' without making source changes ? – Jeson Dias Oct 25 '17 at 07:41
  • If you don't explicitly put the keyword `default` will there still be a default export available in that file? if so what are the rules. – Simon_Weaver Sep 30 '18 at 20:04
23

I was trying to solve the same problem, but found an interesting advice by Basarat Ali Syed, of TypeScript Deep Dive fame, that we should avoid the generic export default declaration for a class, and instead append the export tag to the class declaration. The imported class should be instead listed in the import command of the module.

That is: instead of

class Foo {
    // ...
}
export default Foo;

and the simple import Foo from './foo'; in the module that will import, one should use

export class Foo {
    // ...
}

and import {Foo} from './foo' in the importer.

The reason for that is difficulties in the refactoring of classes, and the added work for exportation. The original post by Basarat is in Avoid Export Default

Matthias
  • 13,607
  • 9
  • 44
  • 60
10

Named export

In TypeScript you can export with the export keyword. It then can be imported via import {name} from "./mydir";. This is called a named export. A file can export multiple named exports. Also the names of the imports have to match the exports. For example:

// foo.js file
export class foo{}
export class bar{}

// main.js file in same dir
import {foo, bar} from "./foo";

The following alternative syntax is also valid:

// foo.js file
function foo() {};
function bar() {};
export {foo, bar};

// main.js file in same directory
import {foo, bar} from './foo'

Default export

We can also use a default export. There can only be one default export per file. When importing a default export we omit the square brackets in the import statement. We can also choose our own name for our import.

// foo.js file
export default class foo{}

// main.js file in same directory
import abc from "./foo";

It's just JavaScript

Modules and their associated keyword like import, export, and export default are JavaScript constructs, not TypeScript. However, TypeScript added the exporting and importing of interfaces and type aliases to it.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
3

Here's an example with simple object exporting.

var MyScreen = {

    /* ... */

    width : function (percent){

        return window.innerWidth / 100 * percent

    }

    height : function (percent){

        return window.innerHeight / 100 * percent
    }

};

export default MyScreen

In the main file (use it when you don't want and don't need to create a new instance) and it is not global, you will import this only when it is needed:

import MyScreen from "./module/screen";
console.log(MyScreen.width(100));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nikola Lukic
  • 4,001
  • 6
  • 44
  • 75