ES Modules in TypeScript
ES Modules in TypeScript
Level 11 — Modules, Declaration Files & Configuration How TypeScript implements the standardized ECMAScript module system (
importandexport) to share code and types between different files.
1. Prerequisites
- Modules (import/export) — The fundamental standard that TypeScript is built upon.
2. Term Category
TypeScript Module System (ES Module Import & Export Syntax): Modules scope variable and type declarations locally, requiring explicit import and export statements to share code.
3. Explanation
4. Common Mistakes & Pitfalls
Mistake 1: Omitting File Extensions in Relative Imports under NodeNext
// ❌ INCORRECT under NodeNext:
// import { helper } from "./helper"; // Compile Error!
// ✅ CORRECT (Must include .js extension even in .ts source files):
import { helper } from "./helper.js";
Why it's wrong: Modern Node.js ES Module resolution (NodeNext) requires explicit .js file extensions in relative import paths.
Golden Rule: Always append .js file extensions to relative import paths when targetting NodeNext.
Mistake 2: Accidental Global Script Pollution by Omitting import / export
// script.ts (Contains no top-level import/export)
const globalConfig = { port: 8080 }; // Leaks into global scope across project!
Why it's wrong: TypeScript treats files without top-level import or export statements as global scripts, polluting global scope.
Golden Rule: Add export {} at the top or bottom of standalone files to enforce module scoping.
Mistake 3: Mixing CommonJS require() with ES Module import
// ❌ INCORRECT: Mixing require with ES module syntax
// const fs = require("fs");
// export function readFile() {}
// ✅ CORRECT (Use ES module import syntax throughout):
import fs from "fs";
export function readFile() {}
Why it's wrong: Mixing CommonJS require() and ES import/export leads to module resolution ambiguity and bundler compilation warnings.
Golden Rule: Use standard ES import/export syntax consistently across TypeScript modules.
5. Practice Exercises
Exercise 1: Exporting and Importing Named & Default Module Members
Scenario:
Export named and default functions from mathUtils.ts and import them in app.ts.
Requirements:
- Use
export defaultandexport const.
Answer
Implementation
// mathUtils.ts
export const PI = 3.14159;
export function add(a: number, b: number): number { return a + b; }
export default function multiply(a: number, b: number): number { return a * b; }
// app.ts
import multiply, { PI, add } from "./mathUtils.js";
console.log(add(5, 10));
console.log(multiply(2, 4));
Technical Explanation
- Named exports (
export const PI) require exact curly brace matching during import (import { PI }). - Default exports (
export default) are imported without curly braces and can be renamed freely at import sites. - Standard ES module syntax.
Exercise 2: Re-Exporting Modules from Index Barrel Files
Scenario:
Create an index.ts barrel file that re-exports all components from sub-modules.
Requirements:
- Use
export * from "./Component.js".
Answer
Implementation
// components/index.ts
export * from "./Button.js";
export * from "./Card.js";
export { default as Modal } from "./Modal.js";
Technical Explanation
- Barrel export files (
index.ts) consolidate exports from multiple internal sub-modules into a single public import entry point. - Simplifies import paths for external consumers (
import { Button, Card } from "./components"). - Standard module architecture pattern.
Exercise 3: Auditing Global Script vs Module Scope
Scenario:
Explain why a TypeScript file containing NO import or export statements is treated as a global script instead of a module.
Requirements:
- Show why top-level variables leak into global scope without
export {}.
Answer
Implementation
// script.ts (Contains NO import/export):
const globalVar = "I leak into global scope!";
// Fix: Add empty export to force ES module scoping:
export {};
Technical Explanation
- Files without top-level
importorexportstatements are evaluated as global scripts by TypeScript. - Top-level variables in global scripts collide across files sharing the same project context.
- Adding
export {}forces TypeScript to treat the file as a scoped ES module.
6. Related Terms
- Namespaces — TypeScript's outdated, legacy module system that was used before ES Modules became the standard.
7. Key Takeaways
- ES Modules (
import/export) scope variables locally within files. NodeNextmodule resolution requires explicit.jsextensions in relative import paths.- Add
export {}to force module scoping on standalone TypeScript files. - Use index barrel files (
index.ts) to clean up public module exports.