Module Systems & Namespaces
📦 Module Systems & Namespaces
🌍 ES Modules: The Standard
ES modules (ESM) are the modern standard. Files are modules; imports/exports are explicit:
Named Exports
export function foo() — export by name. Import with { foo }.
Default Export
export default class User — one default per file. Import as import User from.
Re-export
export { foo } from "./module" — pass-through import/export (barrel exports).
Namespace Import
import * as math from "./math" — import all as math.add().
📼 CommonJS: Node.js Legacy
CommonJS (CJS) is Node.js's original module system. Still widely used, but ES modules are preferred:
ES Modules vs CommonJS
CommonJS (require/module.exports)
- Dynamic (runtime)
- Synchronous
- Node.js built-in
ES Modules (import/export)
- Static (compile-time)
- Asynchronous
- JavaScript standard
"module": "ESNext" in tsconfig.json). CommonJS is legacy but still needed for older Node.js versions.
🗺️ Path Mapping: Cleaner Imports
Deep nested paths are messy. Path mapping lets you create short aliases:
@ prefix is common in modern projects (similar to npm scopes). Use @utils, @types, @components for clarity. Keep the mapping shallow so paths stay readable.
📦 Barrel Exports: Organized Re-exports
A barrel export (index.ts) re-exports from submodules for cleaner public APIs:
Without barrel: You'd need two imports. With barrel: One import gets everything.
📋 Ambient Declarations: Types for External Code
Sometimes you use libraries without type definitions. Ambient declarations (`.d.ts` files) provide types:
🔷 TypeScript Namespaces (Legacy Grouping)
Namespaces group related types without ES modules. They're pre-module, but still used in legacy codebases:
🏗️ Real-World Pattern: Well-Organized Project
Clean Project Structure with Path Aliases & Barrels
💻 Coding Challenges
Challenge 1: Create a Barrel Export
Create three utility modules (math.ts, string.ts, array.ts) with exported functions. Then create an index.ts barrel that re-exports all. Test importing from the barrel.
Goal: Practice barrel exports and namespace organization.
Challenge 2: Set Up Path Aliases
Create a tsconfig.json with path aliases (@utils, @types, @services). Set up dummy modules under each and import using the aliases.
Goal: Configure and use path mapping.
Challenge 3: Ambient Declaration
Create a .d.ts file that declares a global DEBUG constant and a module type for an external library. Write code that uses both.
Goal: Practice ambient declarations for untyped external code.
Be careful with circular imports: A imports B, B imports A. TypeScript won't error, but at runtime it can cause undefined values. Refactor to break the cycle—move shared types to a third file both can import from.
🎯 What's Next
With module systems mastered, we'll explore Advanced OOP — abstract classes, access modifiers, static members, and design patterns in TypeScript.