TypeScript - Modules
TypeScript - Modules (ES Modules)
Section titled “TypeScript - Modules (ES Modules)”Modules are a fundamental concept in modern JavaScript and TypeScript for organizing code into reusable, independent pieces. They help manage dependencies, prevent global namespace pollution, and improve maintainability by encapsulating code.
TypeScript fully embraces the standard ECMAScript (ES) Module system, which uses the import and export keywords. This is the recommended approach for structuring TypeScript applications for both browsers and Node.js environments.
ES Modules: export and import
Section titled “ES Modules: export and import”Any file containing a top-level import or export statement is considered a module. Variables, functions, classes, etc., declared in a module are local to that module unless explicitly exported.
Exporting Members
Section titled “Exporting Members”You can export declarations by adding the export keyword.
// --- mathUtils.ts ---export const PI = 3.14159;
export function add(x: number, y: number): number { return x + y;}
export interface Point { x: number; y: number;}
// Default export (only one per module)export default class Calculator { add(x: number, y: number): number { return x + y; } // ... other methods}You can also export members separately:
// --- stringUtils.ts ---function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.slice(1);}
function reverse(str: string): string { return str.split('').reverse().join('');}
export { capitalize, reverse }; // Exporting named membersexport { capitalize as cap }; // Exporting with a different nameImporting Members
Section titled “Importing Members”Use the import keyword to access exported members from other modules. The path specified is usually relative or can be resolved by the module loader/bundler (e.g., from node_modules).
// --- main.ts ---
// Importing named exportsimport { add, PI, Point } from './mathUtils';
// Importing a default export (can use any name)import MyCalculator from './mathUtils';
// Importing named exports with aliasimport { capitalize as makeFirstLetterUpper, reverse } from './stringUtils';
// Importing the entire module as an objectimport * as math from './mathUtils';
console.log(`Adding 5 and 3: ${add(5, 3)}`); // Output: 8console.log(`PI is approx: ${PI}`); // Output: 3.14159
const origin: Point = { x: 0, y: 0 };console.log('Origin:', origin);
const calc = new MyCalculator();console.log(`Calculator adds 10 and 20: ${calc.add(10, 20)}`); // Output: 30
console.log(makeFirstLetterUpper('hello')); // Output: Helloconsole.log(reverse('world')); // Output: dlrow
console.log(`Accessing via module object: ${math.add(1, 1)}`); // Output: 2console.log(`Default export via module object: ${new math.default().add(2,2)}`); // Output: 4Module Resolution and Execution
Section titled “Module Resolution and Execution”How modules are found (resolution) and executed depends on the target environment:
- Browsers: Modern browsers support ES Modules directly using
<script type="module" src="...">. However, for compatibility, dependency management, and optimization, developers typically use bundlers like Webpack, Rollup, Parcel, or Vite. Bundlers processimport/exportstatements, resolve dependencies, and create optimized JavaScript files for the browser. - Node.js: Node.js has built-in support for both ES Modules (using
.mjsfiles or setting"type": "module"inpackage.json) and the older CommonJS module system (require/module.exports). TypeScript can compile to either format using themoduleoption intsconfig.json(e.g.,"module": "NodeNext"or"module": "CommonJS").
tsconfig.json Configuration
Section titled “tsconfig.json Configuration”Key options in tsconfig.json related to modules include:
"module": Specifies the module system for the compiled JavaScript output (e.g.,"ES2020","CommonJS","NodeNext")."target": Specifies the ECMAScript version for the output code (influences features available)."moduleResolution": How the compiler finds modules (usually"Node"or"NodeNext")."outDir": The directory where compiled JavaScript files are placed."rootDir": Specifies the root directory of source files (helps maintain structure inoutDir).
Namespaces (Formerly Internal Modules)
Section titled “Namespaces (Formerly Internal Modules)”TypeScript also has a concept called namespace. Before ES Modules became standard, namespaces were the primary way in TypeScript to group related code and avoid polluting the global scope. They compile to nested JavaScript objects or Immediately Invoked Function Expressions (IIFEs).
While ES Modules are now the standard for organizing code and managing dependencies, namespaces can still be useful for:
- Organizing internal code within a large project where modules might feel too granular.
- Structuring declaration files (
.d.ts) for libraries that expose global objects. - Working with older codebases or specific build systems that rely on
--outFilecompilation (though this is less common now).
Namespace Syntax
Section titled “Namespace Syntax”namespace Validation { export interface StringValidator { isAcceptable(s: string): boolean; }
const lettersRegexp = /^[A-Za-z]+$/;
export class LettersOnlyValidator implements StringValidator { isAcceptable(s: string) { return lettersRegexp.test(s); } }}
// Usage:let validator: Validation.StringValidator = new Validation.LettersOnlyValidator();console.log(validator.isAcceptable("HelloWorld")); // trueconsole.log(validator.isAcceptable("12345")); // falseIf namespaces span multiple files, you might need /// <reference path="..." /> directives or use the --outFile compiler option, but again, ES Modules handle multi-file organization more naturally.
Recommendation
Section titled “Recommendation”For modern TypeScript development, strongly prefer ES Modules (import/export) over namespaces for code organization and dependency management. Understand namespaces primarily for compatibility with older code or specific use cases like global declaration merging.