TypeScript Modules
Modern code organization with ES6 modules
📦 What are Modules?
Modules are TypeScript's modern way of organizing code into separate files with explicit imports and exports, providing better code reusability, maintainability, and dependency management using ES6 module syntax.
// File: math.ts
export function add(a: number, b: number) {
return a + b;
}
// File: main.ts
import { add } from './math';
console.log(add(5, 3)); // Output: 8
Output:
8
Key Module Concepts
Named Exports
Export multiple items by name
export const PI = 3.14;
export function add() {}
Default Export
Export one main item per file
export default class User {
// class definition
}
Import Syntax
Import exported items
import { add } from './math';
import User from './user';
Re-exports
Export from other modules
export { add } from './math';
export * from './utils';
🔹 Named Exports
Export multiple items from a module:
// File: mathUtils.ts
export const PI = 3.14159;
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
export class Calculator {
multiply(a: number, b: number): number {
return a * b;
}
}
// Alternative export syntax
const divide = (a: number, b: number) => a / b;
const EULER = 2.71828;
export { divide, EULER };
// File: main.ts
import { add, subtract, PI, Calculator } from './mathUtils';
console.log(add(5, 3)); // Output: 8
console.log(subtract(10, 4)); // Output: 6
console.log(PI); // Output: 3.14159
const calc = new Calculator();
console.log(calc.multiply(4, 5)); // Output: 20
Output:
8
6
3.14159
20
🔹 Default Exports
Export a single main item from a module:
// File: User.ts
export default class User {
constructor(public name: string, public age: number) {}
greet() {
return `Hello, I'm ${this.name}`;
}
}
// File: config.ts
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
export default config;
// File: logger.ts
export default function log(message: string) {
console.log(`[LOG]: ${message}`);
}
// File: main.ts
import User from './User';
import config from './config';
import log from './logger';
const user = new User('Alice', 25);
console.log(user.greet()); // Output: Hello, I'm Alice
log('Application started'); // Output: [LOG]: Application started
console.log(config.apiUrl); // Output: https://api.example.com
Output:
Hello, I'm Alice
[LOG]: Application started
https://api.example.com
🔹 Import Variations
Different ways to import modules:
// Named imports
import { add, subtract } from './math';
// Import with alias
import { add as addition, subtract as subtraction } from './math';
// Import all as namespace
import * as MathUtils from './math';
console.log(MathUtils.add(5, 3));
// Default import
import User from './User';
// Mix default and named imports
import User, { validateUser, UserRole } from './User';
// Import for side effects only (no bindings)
import './polyfills';
// Dynamic import (async)
async function loadModule() {
const module = await import('./heavyModule');
module.doSomething();
}
// Import type only (TypeScript specific)
import type { UserType } from './types';
// Import both type and value
import { type User, createUser } from './user';
🔹 Re-exports
Export items from other modules:
// File: shapes/circle.ts
export class Circle {
constructor(public radius: number) {}
}
// File: shapes/square.ts
export class Square {
constructor(public side: number) {}
}
// File: shapes/index.ts (barrel export)
export { Circle } from './circle';
export { Square } from './square';
export * from './triangle';
// Or export with alias
export { Circle as CircleShape } from './circle';
// File: main.ts
// Now import from single entry point
import { Circle, Square } from './shapes';
const circle = new Circle(5);
const square = new Square(10);
// Without barrel export, you'd need:
// import { Circle } from './shapes/circle';
// import { Square } from './shapes/square';
🔹 Module Resolution
How TypeScript finds and loads modules:
// Relative imports (start with ./ or ../)
import { User } from './models/User';
import { config } from '../config';
// Non-relative imports (node_modules or paths)
import express from 'express';
import { Component } from '@angular/core';
// File extensions (optional in TypeScript)
import { add } from './math'; // Looks for math.ts, math.tsx, math.d.ts
import { add } from './math.js'; // Specific file
// Index files
import { User } from './models'; // Looks for models/index.ts
// Path mapping (tsconfig.json)
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@models/*": ["models/*"],
"@utils/*": ["utils/*"]
}
}
}
// Then use:
import { User } from '@models/User';
import { log } from '@utils/logger';
🔹 Practical Example
Real-world module organization:
// File: models/User.ts
export interface IUser {
id: number;
name: string;
email: string;
}
export class User implements IUser {
constructor(
public id: number,
public name: string,
public email: string
) {}
}
// File: services/UserService.ts
import { User, IUser } from '../models/User';
export class UserService {
private users: User[] = [];
addUser(user: IUser): void {
this.users.push(new User(user.id, user.name, user.email));
}
getUser(id: number): User | undefined {
return this.users.find(u => u.id === id);
}
getAllUsers(): User[] {
return this.users;
}
}
export default new UserService();
// File: utils/logger.ts
export function log(message: string): void {
console.log(`[${new Date().toISOString()}] ${message}`);
}
export function error(message: string): void {
console.error(`[ERROR] ${message}`);
}
// File: index.ts (barrel export)
export * from './models/User';
export * from './services/UserService';
export * from './utils/logger';
// File: main.ts
import userService from './services/UserService';
import { log } from './utils/logger';
userService.addUser({ id: 1, name: 'Alice', email: '[email protected]' });
log('User added successfully');
const user = userService.getUser(1);
console.log(user?.name); // Output: Alice
Output:
[2025-01-15T10:30:00.000Z] User added successfully
Alice