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

🧠 Test Your Knowledge

How many default exports can a module have?