Default Exports in TypeScript
Default exports let a TypeScript module export a single main value. Learn how they work, when to use them, and the tradeoffs compared to named exports.
Default exports in TypeScript let a module expose one primary value as its main export. Unlike named exports, where the importer must use the exact exported name, a default export lets the importing file choose its own local name. Every module can have at most one default export.
Here is the simplest default export. The first file exports a function as its default, and the second imports it under a different local name:
// greet.ts
export default function greet(name: string): string {
return `Hello, ${name}!`;
}The importer can choose any name it likes because a default import has no name to check against. Here the original greet function becomes sayHello in the consuming file:
// app.ts
import sayHello from "./greet.js";
console.log(sayHello("TypeScript"));The original function is called greet, but the importer names it sayHello. Both names work, because the compiled JavaScript does not preserve the original name as part of the module binding. Running this code prints Hello, TypeScript! to the console.
TypeScript checks that the default export exists and that its type signature matches how you use it, but it does not enforce that all importers use the same name. This flexibility is the defining tradeoff of default exports.
Default Export Syntax
TypeScript supports several ways to define a default export. Each has a different use case.
Inline default export is the most common pattern. Put export default directly before a function or class declaration.
// Calculator.ts
export default class Calculator {
add(a: number, b: number): number {
return a + b;
}
multiply(a: number, b: number): number {
return a * b;
}
}Default export after declaration is useful when the name matters for internal references within the module. Declare first, then export separately.
// config.ts
const appConfig = {
port: 3000,
host: "localhost",
debug: true,
};
export default appConfig;Default export of a type has a syntax constraint: you cannot write export default interface. Instead, define the type first and export it separately, or use a type alias with the export default type form.
Typing a Default Export
When you export a function or class as default, TypeScript infers the type automatically. You can also add an explicit type annotation when the inferred type is not precise enough.
// formatter.ts
export default function format(
value: number,
locale: string = "en-US"
): string {
return value.toLocaleString(locale);
}For object defaults, TypeScript infers the shape, but you can make it explicit with a type annotation on the variable. An explicit type gives better autocomplete when consuming the default export and catches property typos in the object literal.
Re-Exporting a Default Export
You can re-export the default export from another module. There are two patterns for this.
Re-export a named export as the default of the current module. This forwards a named export from one module as the default export of another. First, a module with a named export:
// math.ts
export function add(a: number, b: number): number {
return a + b;
}Now a barrel file that re-exports that named function as its own default, so consumers can import it without curly braces:
// index.ts
export { add as default } from "./math.js";Alternatively, pass through another module's default unchanged. Use export { default } from to re-export a default as a default.
// index.ts
export { default } from "./math.js";This passes through the default export unchanged. Consumers of index.ts get the same default as if they had imported from math.ts directly. For more on this pattern, see barrel files in TypeScript.
Default Export with Named Exports
A module can have both a default export and named exports. This pattern is common in libraries that provide a main function plus configuration helpers.
// api.ts
interface RequestConfig {
baseURL: string;
timeout: number;
}
export const defaultConfig: RequestConfig = {
baseURL: "https://api.example.com",
timeout: 5000,
};
export default function api(path: string) {
return fetch(`${defaultConfig.baseURL}${path}`);
}Consumers can take just the default if that is all they need, or grab the named exports for advanced usage. The default import is the main function, and named imports like defaultConfig are secondary helpers.
Default Exports vs Named Exports
Each style has strengths. The choice depends on how your module is consumed.
| Scenario | Better choice | Reason |
|---|---|---|
| Module exports one main thing | Default export | Clear intent: this is the point of this module |
| Module exports several utilities | Named exports | Each utility is independently accessible |
| Refactoring across many files | Named exports | Rename refactoring works project-wide |
| Tree-shaking is critical | Named exports | Bundlers can eliminate unused named exports more reliably |
| Framework component file | Default export | Convention in React, Vue, Svelte, and similar |
| Library public API | Named exports | Consumers get autocomplete and consistent names |
Named exports give better tooling support because every import site uses the same name. Default exports give more flexibility because each importer can choose a name that fits its local context. For a deeper comparison, see named exports in TypeScript.
Common Mistakes
Forgetting the curly braces with named imports. If a module uses a default export and you try to import it with curly braces, TypeScript reports that the module has no named export with that name. Use import add from "./math.js" (no curly braces) for default exports.
Using different names for the same default export across files. Since TypeScript does not enforce consistent naming for default imports, you can end up with import api from './api.js' in one file and import client from './api.js' in another. This makes find-all-references less useful. Agree on a consistent name across the project.
Exporting a default that is too complex. If your default export is a large object with many unrelated properties, consider splitting it into named exports. A default export should represent one clear concept that stands on its own.
Trying to use export default interface inline. This syntax is not valid TypeScript. The compiler rejects it with a clear error. Define the interface first, then export it as default in a separate statement.
Rune AI
Key Insights
- A default export is the primary export of a module, imported without curly braces.
- The importing file chooses the local name. TypeScript does not enforce consistency across files.
- Default exports can be functions, classes, objects, or values, and can include a type annotation.
- Use export default for single-purpose modules like components or configuration objects.
- Prefer named exports when a module shares multiple values or when tooling precision matters.
Frequently Asked Questions
Can a TypeScript module have both default and named exports?
Does the import name need to match the original function name for a default export?
Can I export a type as the default export?
Conclusion
Default exports are a concise way to share a single main value from a module. They work well for components, classes, and single-purpose modules. However, they trade tooling precision for import flexibility. For most TypeScript projects, prefer named exports for shared utilities and reserve default exports for modules with one clear primary export.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.