TypeScript Default Exports

In TypeScript, default exports provide a way to designate a single "main" value or entity that a module provides. While a module can have many named exports, it can only have one default export. This approach is common in modern JavaScript frameworks like React, where a single file often represents a single component.

Developer Tip: Think of a default export as the "primary personality" of your file. If your file is named UserService.ts, the UserService class is the natural candidate for the default export.

 

What is Default Export?

A default export is a declaration that tells TypeScript: "If someone imports from this file without specifying exactly what they want, give them this." Unlike named exports, you do not use curly braces { } when importing them, and you have the flexibility to name the import whatever you like in the receiving file.

Watch Out: You can only have one export default per file. Attempting to define two will result in a compilation error.

 

Exporting Default in TypeScript

To export a default entity, you use the export default keyword. This can be applied to functions, classes, variables, or objects. You can even export anonymous functions or classes as defaults, though giving them names is better for debugging.

Example 1: Exporting a Function as Default

// file: add.ts

/**
 * A simple utility to add two numbers.
 * Because this is the only logic in the file, 
 * it makes sense as a default export.
 */
export default function add(x: number, y: number): number {
  return x + y;
}
  • In this case, the add function is the primary tool provided by the add.ts module.
Best Practice: Use default exports for standalone utility functions or components to make the import syntax cleaner for other developers.

Example 2: Exporting a Class as Default

// file: Calculator.ts

export default class Calculator {
  static multiply(x: number, y: number): number {
    return x * y;
  }
}
  • Classes are frequently used as default exports, especially when following the "one class per file" design pattern common in Object-Oriented Programming.

Example 3: Exporting an Object as Default

// file: config.ts

const config = {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retryAttempts: 3
};

export default config;
  • This is a common way to share configuration settings or environment-specific constants across an entire application.

 

Importing Default Exports in TypeScript

When importing from a module that has a default export, you omit the curly braces. This makes the code look cleaner and signals to other developers that you are pulling in the "main" functionality of that module.

Example 1: Importing a Default Export (Function)

// file: app.ts
import add from './add';

console.log(add(5, 3)); // Output: 8
  • The add function is imported directly. Notice the absence of { add }.
Common Mistake: Trying to import a default export using curly braces like import { add } from './add'. This will fail because TypeScript will look for a named export called "add" rather than the default export.

Example 2: Importing a Default Export (Class)

// file: app.ts
import Calculator from './Calculator';

console.log(Calculator.multiply(5, 3)); // Output: 15

Example 3: Importing a Default Export (Object)

// file: app.ts
import config from './config';

console.log(config.apiUrl); // Output: https://api.example.com

 

Renaming Default Imports

One of the unique features of default exports is that the name you use during import does not have to match the name used in the source file. This is useful for avoiding naming collisions or providing more context.

Example: Renaming Default Import

// file: app.ts
import sumLogic from './add';

// We imported 'add', but renamed it to 'sumLogic' locally
console.log(sumLogic(5, 3)); // Output: 8
  • This is helpful if you are importing functions from different modules that might share common names, like init or handleUpdate.
Developer Tip: While you can rename default imports, doing it too often can make a codebase harder to search. Try to keep names consistent across your project unless there is a specific conflict.

 

Default Export vs Named Exports

It is important to understand when to use each. Default exports are for the "main" thing in a file. Named exports are for utility libraries where a single file might contain dozens of independent helper functions.

Example: Using Default and Named Exports Together

// file: math.ts

// The primary function (Default)
export default function add(x: number, y: number): number {
  return x + y;
}

// Supplementary constants (Named)
export const pi = 3.1415;
export const e = 2.718;
  • In this scenario, add is the star of the show, while pi and e are secondary utilities.
// file: app.ts
import add, { pi, e } from './math';

console.log(add(5, 3)); 
console.log(pi);        
  • When importing both, the default export always sits outside the curly braces, and the named exports are listed inside them.
Best Practice: Many large-scale TypeScript projects actually prefer Named Exports even for primary entities because they work better with "Rename Symbol" refactoring tools in IDEs like VS Code.

 

Summary

  • Default Exports: Use export default to share a single primary entity (class, function, or object) from a file.
  • Clean Imports: You don't need curly braces for default imports, leading to cleaner-looking code.
  • Naming Flexibility: You can choose any name for a default import in the consuming file.
  • Hybrid Modules: You can mix one default export with multiple named exports in the same module for maximum flexibility.

By using default exports strategically, you can create a clear hierarchy in your code, making it obvious which parts of a module are meant to be the primary interface and which are supporting utilities.