TypeScript Modules
In modern JavaScript and TypeScript development, modules are the core mechanism for organizing code, managing dependencies, and avoiding global scope pollution. Starting with ES6 (ECMAScript 2015), JavaScript natively supports a module system, and TypeScript fully supports and extends this system.
For all new projects, modules are the recommended way to organize code.
What are Modules?
In TypeScript, any file containing a top-level import or export declaration is considered a module. Each module has its own scope, and variables, functions, classes, etc. declared in a module are not visible outside the module unless they are explicitly exported.
export: Exporting Module Members
You can use the export keyword to make members in a module accessible to other modules.
math.ts
// Export a constant
export const PI = 3.14159;
// Export an interface
export interface Calculation {
(x: number, y: number): number;
}
// Export a function
export function add(x: number, y: number): number {
return x + y;
}
// Export a class
export class Subtract {
calculate(x: number, y: number): number {
return x - y;
}
}import: Importing Module Members
You can use the import keyword to import exported members from another module.
app.ts
// Import specified members from the './math' module
import { PI, add, Subtract } from './math';
console.log(PI); // 3.14159
let result = add(10, 5);
console.log(result); // 15
let subtractor = new Subtract();
console.log(subtractor.calculate(20, 8)); // 12Default Exports
Each module can also have a "default" export. Default exports have more concise syntax when importing. A module can only have one default export.
calculator.ts
export default class Calculator {
add(x: number, y: number): number {
return x + y;
}
// ...other methods
}
// You can still have other named exports
export const version = "1.0";Importing Default Exports
When importing a default export, you can give it any name.
main.ts
// Import the default exported Calculator class and name it MyCalc
import MyCalc, { version } from './calculator';
const calc = new MyCalc();
console.log(calc.add(5, 5)); // 10
console.log(`Version: ${version}`); // Version: 1.0Other Import/Export Syntax
Importing All Members
You can use the * as name syntax to import all exported members of a module into an object.
import * as math from './math';
console.log(math.PI);
let res = math.add(2, 3);Re-exporting
Sometimes, you might want to create a module that aggregates exports from several other modules. You can import and then export, or directly re-export.
utils.ts
// Re-export all content from stringUtils.ts
export * from './stringUtils';
// Re-export 'isEven' from numberUtils.ts
export { isEven } from './numberUtils';Module Resolution
When you say import { a } from "moduleB", the TypeScript compiler needs to know which file moduleB refers to. This process is called module resolution.
TypeScript supports two main module resolution strategies:
- Classic: The default strategy in earlier versions, rarely used now.
- Node: The resolution strategy used by Node.js, now the standard for most projects. It looks for relative paths (
./moduleB) or searches innode_modulesfolders for non-relative paths (moduleB).
You can configure this strategy in tsconfig.json through the moduleResolution option.
{
"compilerOptions": {
"module": "CommonJS",
"moduleResolution": "Node"
}
}Module Formats
TypeScript code ultimately needs to be compiled to JavaScript in a specific module format to run in the target environment (like browsers or Node.js). Common module formats include:
- CommonJS: The main format used by Node.js.
- AMD (Asynchronous Module Definition): Used in libraries like RequireJS.
- ES6 / ES2015: The native module format supported by modern browsers and bundling tools.
You can specify the output module format in the module option of tsconfig.json. For modern web development, it's typically set to ESNext, then handled by bundling tools (like Vite or Webpack) to produce browser-compatible code.