← Back to Course

📦 Modules & Namespaces

Modules help organize TypeScript code into separate files with explicit imports and exports. TypeLand uses ES2020 modules to structure the Adventure Game and Quiz code across multiple files!

ES6 Modules (Recommended)

Modern TypeScript uses ES6 module syntax with import/export:

Example: Exporting from a Module

// File: types.ts
export interface Player {
  name: string;
  level: number;
  health: number;
}

export enum Direction {
  North,
  South,
  East,
  West
}

export function createPlayer(name: string): Player {
  return {
    name,
    level: 1,
    health: 100
  };
}

// Default export (one per file)
export default class Game {
  private players: Player[] = [];
  
  addPlayer(player: Player): void {
    this.players.push(player);
  }
}

Example: Importing from a Module

// File: main.ts

// Named imports
import { Player, Direction, createPlayer } from './types.js';

// Default import
import Game from './types.js';

// Import everything as namespace
import * as Types from './types.js';

// Using imports
const player: Player = createPlayer("Hero");
const game = new Game();
game.addPlayer(player);

// Using namespace import
const direction: Types.Direction = Types.Direction.North;

TypeLand's Module Structure

Here's how the TypeLand Adventure Game organizes its modules:

Example: TypeLand Module Organization

// File: definitions.ts - Core types and enums
export type Coordinate = [number, number];

export enum MovementKey {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST"
}

export enum GroundMaterial {
  MEADOW = "MEADOW",
  STONE = "STONE",
  LAKE = "LAKE",
  SAND = "SAND"
}

// ---

// File: hero.ts - Hero class
import { Coordinate, CollectibleKind } from './definitions.js';

export class Hero {
  private position: Coordinate;
  private backpack: CollectibleKind[];
  
  constructor(startPos: Coordinate) {
    this.position = startPos;
    this.backpack = [];
  }
  
  getPosition(): Coordinate {
    return this.position;
  }
}

// ---

// File: terrain.ts - WorldTerrain class
import { Coordinate, GroundMaterial } from './definitions.js';

export class WorldTerrain {
  private grid: GroundMaterial[][];
  
  getMaterial(pos: Coordinate): GroundMaterial {
    const [row, col] = pos;
    return this.grid[row][col];
  }
}

// ---

// File: adventure.ts - Main game engine
import { Hero } from './hero.js';
import { WorldTerrain } from './terrain.js';
import { MovementKey, Coordinate } from './definitions.js';

export class AdventureEngine {
  private hero: Hero;
  private world: WorldTerrain;
  
  constructor() {
    this.hero = new Hero([7, 7]);
    this.world = new WorldTerrain();
  }
  
  moveHero(direction: MovementKey): boolean {
    // Movement logic
    return true;
  }
}

Export Types

Different ways to export from modules:

Example: Export Variations

// Named exports (multiple per file)
export const PI = 3.14159;
export function add(a: number, b: number): number {
  return a + b;
}

// Export existing declarations
const VERSION = "1.0.0";
function multiply(a: number, b: number): number {
  return a * b;
}
export { VERSION, multiply };

// Export with rename
const internalName = "TypeLand";
export { internalName as gameName };

// Re-export from another module
export { Player, Game } from './types.js';
export * from './utils.js';  // Re-export all

// Default export (one per file)
export default class Application {
  // Main application class
}

Import Types

Example: Import Variations

// Named imports
import { Player, Game } from './types.js';

// Import with rename
import { gameName as appName } from './constants.js';

// Default import (name it whatever you want)
import App from './app.js';
import MyApp from './app.js';  // Same thing, different name

// Combined default and named imports
import Game, { Player, Level } from './game.js';

// Import all as namespace
import * as Utils from './utils.js';
Utils.formatDate(new Date());

// Type-only imports (removed in JavaScript output)
import type { Player } from './types.js';
import { type Game, startGame } from './game.js';

Module Resolution

TypeScript needs to find your modules:

Example: Import Path Styles

// Relative imports (most common)
import { Player } from './types.js';           // Same directory
import { Game } from '../game.js';             // Parent directory
import { Utils } from '../../utils/helpers.js'; // Up two levels

// tsconfig.json configuration for module resolution
{
  "compilerOptions": {
    "module": "ES2020",           // Module format
    "moduleResolution": "node",   // How to resolve imports
    "baseUrl": "./src",           // Base directory
    "paths": {                    // Path aliases
      "@models/*": ["models/*"],
      "@utils/*": ["utils/*"]
    }
  }
}

// Using path aliases
import { Player } from '@models/player.js';
import { formatDate } from '@utils/date.js';

Namespaces (Legacy)

Namespaces are TypeScript's older module system. Use ES6 modules instead for new code:

Example: Namespaces (for reference)

// Namespace (avoid in new code)
namespace Game {
  export interface Player {
    name: string;
    score: number;
  }
  
  export class Engine {
    start(): void {
      console.log("Game starting...");
    }
  }
}

// Usage
const player: Game.Player = { name: "Hero", score: 0 };
const engine = new Game.Engine();

// Prefer ES6 modules instead!
// They have better tooling support and tree-shaking

Best Practices

Example: Module Best Practices

// ✓ GOOD: Clear, single-purpose modules

// File: player.ts
export interface Player {
  id: number;
  name: string;
  health: number;
}

export function createPlayer(name: string): Player {
  return { id: Date.now(), name, health: 100 };
}

// ---

// File: game.ts
import { Player, createPlayer } from './player.js';

export class Game {
  private players: Player[] = [];
  
  addPlayer(name: string): Player {
    const player = createPlayer(name);
    this.players.push(player);
    return player;
  }
}

// ✗ AVOID: Circular dependencies
// File A imports File B, File B imports File A

// ✓ GOOD: Use index files for cleaner imports

// File: models/index.ts
export { Player } from './player.js';
export { Game } from './game.js';  
export { Level } from './level.js';

// Now import from one place:
import { Player, Game, Level } from './models/index.js';

🎯 Key Concepts

  • Use ES6 modules (import/export) for all new TypeScript code
  • Each file is a module with its own scope
  • Use export to make things available to other modules
  • Use import to bring in code from other modules
  • Default exports are useful for main class/function per file
  • Relative imports (./, ../) are most common
  • TypeLand uses modules to organize game components!
  • Avoid circular dependencies between modules
← Back to Intermediate Course