Skip to content
beginner Phase 3 · TypeScript Classes & Modules

Modules & Namespaces

Organize code with ES modules, import/export, and namespace patterns.

45m
0 problems
Topic Progress 0%

ES Module Syntax

ES Module Syntax

Named Exports

// math.ts
export function add(a: number, b: number): number {
  return a + b;
}

export function subtract(a: number, b: number): number {
  return a - b;
}

export const PI = 3.14159;

export interface Point {
  x: number;
  y: number;
}

export type Result<T> = { success: true; data: T } | { success: false; error: string };

Default Export

// logger.ts
export default class Logger {
  log(message: string): void {
    console.log(`[LOG] ${message}`);
  }
}

// Import default export
import Logger from './logger.js';
const logger = new Logger();

Re-exports

// Re-export from another module
export { add, subtract } from './math.js';
export type { Point } from './types.js';

// Re-export with rename
export { add as sum } from './math.js';

Module Resolution

Module Resolution

TypeScript Configuration

// tsconfig.json
{
  "compilerOptions": {
    "module": "NodeNext",
    "moduleResolution": "NodeNext"
  }
}

Import Styles

// Named import
import { readFile, writeFile } from 'fs/promises';

// Default import
import express from 'express';

// Namespace import
import * as path from 'path';

// Side-effect import
import './polyfills.js';

// Type-only import
import type { Request, Response } from 'express';
import { type User, createUser } from './user.js';

File Extensions

// With NodeNext module resolution, include file extensions
import { helper } from './utils.js'; // not './utils'

// In tsconfig, use .js extension even for .ts files
// The compiler will resolve to the correct .ts file

Barrel Files

Barrel Files

Organizing Exports

// types/index.ts (barrel file)
export type { User } from './user.js';
export type { Post } from './post.js';
export type { Comment } from './comment.js';

// Import from barrel
import type { User, Post, Comment } from './types/index.js';

Selective Barrel Exports

// Avoid exporting everything - be explicit
export { UserService } from './user-service.js';
export type { UserServiceInterface } from './user-service.js';

// Don't do this - leaks internal implementation
// export * from './internal.js';

Index Files

// api/index.ts
export { UserController } from './user-controller.js';
export { PostController } from './post-controller.js';
export { setupRoutes } from './routes.js';

// Clean imports in main file
import { UserController, PostController, setupRoutes } from './api/index.js';

CommonJS Interop

CommonJS Interop

CJS vs ESM

// CommonJS style (Node.js legacy)
const express = require('express');
module.exports = { helper };

// ES Module style (modern)
import express from 'express';
export function helper() {}

Enabling Interop

// tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}

Importing CJS from ESM

// Works with esModuleInterop enabled
import express from 'express';  // CJS module imported as default

// Without esModuleInterop, you would need:
import * as express from 'express';

Package.json Configuration

// For ES modules in Node.js
{
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

Best Practices

  • Always use ES module syntax in TypeScript
  • Enable esModuleInterop in tsconfig
  • Include .js extensions in imports
  • Use type keyword for type-only imports
  • Keep barrel files explicit, avoid export *