Modules are essential for organizing, reusing, and maintaining code in TypeScript. They help encapsulate functionality, avoid global scope pollution, and enable efficient dependency management. Let’s explore named exports, default exports, and other key concepts with practical examples.
Why Use Modules?
-
Encapsulation: Keep variables/functions private unless explicitly exported.
-
Reusability: Share code across files.
-
Structure: Break down complex apps into manageable pieces.
-
Dependency Management: Explicitly declare dependencies.
Key Concepts
TypeScript supports two module systems:
-
ES Modules (Modern:
import
/export
) -
CommonJS (Node.js:
require
/module.exports
).
This guide focuses on ES Modules.
1. Named Exports (Multiple Exports per File)
Export individual values (variables, functions, classes) by name.
Syntax: export { name1, name2, ... }
Example:
// 📁 utils.ts export const PI = 3.14; // Named export (variable) export function sum(a: number, b: number): number { // Named export (function) return a + b; } class Calculator { // Named export (class) static multiply(a: number, b: number): number { return a * b; } } export { Calculator }; // Export declared class
Import Named Exports:
// 📁 app.ts import { PI, sum, Calculator } from './utils'; console.log(PI); // 3.14 console.log(sum(2, 3)); // 5 console.log(Calculator.multiply(4, 5)); // 20
2. Default Export (Single Export per File)
Export one primary value (e.g., a function, class, or object).
Syntax: export default value;
Example:
// 📁 Logger.ts export default class Logger { // Default export (class) static log(message: string): void { console.log(`[LOG] ${message}`); } } // ❌ Avoid multiple default exports in one file!
Import Default Exports:
// 📁 app.ts import Logger from './Logger'; // No braces needed! Logger.log("Hello Modules!"); // [LOG] Hello Modules!
3. Combining Named and Default Exports
Mix both in one module:
// 📁 math.ts export const PI = 3.14; // Named export export default function add(a: number, b: number): number { // Default export return a + b; }
Import Both:
// 📁 app.ts import add, { PI } from './math'; // Default + named console.log(add(PI, 10)); // 13.14
4. Renaming Exports/Imports
Avoid naming collisions with aliases:
// 📁 app.ts import { sum as addNumbers } from './utils'; // Rename named export import Logger as MyLogger from './Logger'; // Rename default export MyLogger.log(addNumbers(1, 2)); // [LOG] 3
5. Re-exporting (Barrel Files)
Aggregate exports from multiple files:
// 📁 math/index.ts export { default as add } from './add'; // Re-export default export { PI } from './constants'; // Re-export named
Usage:
// 📁 app.ts import { add, PI } from './math'; // Clean import path
6. Import Everything as a Namespace
Bundle all exports into a single namespace object:
// 📁 app.ts import * as Utils from './utils'; console.log(Utils.PI); // 3.14 console.log(Utils.sum(1, 2)); // 3
Best Practices
-
Prefer Named Exports:
-
Ensures consistent naming across imports.
-
Facilitates tree-shaking (dead code elimination).
-
-
Use Default Exports Sparingly:
-
Ideal for primary classes/functions (e.g.,
React.Component
).
-
-
Barrel Files:
-
Simplify imports in large projects (e.g.,
import { User, Post } from './models'
).
-
Common Pitfalls
-
Default vs. Named Confusion:
// ❌ Error: Named import for default export import { Logger } from './Logger'; // Incorrect! // ✅ Correct import Logger from './Logger';
-
Circular Dependencies:
Avoid modules importing each other (e.g.,A → B → A
).
Conclusion
Modules are the backbone of scalable TypeScript applications:
-
Named Exports: For multiple values.
-
Default Exports: For a module’s primary functionality.
-
Re-exports: Simplify dependency graphs.
By mastering these patterns, you’ll write cleaner, more maintainable code. 🚀
Pro Tip: Configure tsconfig.json
with "module": "ES2015"
(or higher) for modern projects!