+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 271 of 355

📘 Source Code Documentation: TSDoc

Master source code documentation: tsdoc in TypeScript with practical examples, best practices, and real-world applications 🚀

🚀Intermediate
25 min read

Prerequisites

  • Basic understanding of JavaScript 📝
  • TypeScript installation ⚡
  • VS Code or preferred IDE 💻

What you'll learn

  • Understand the concept fundamentals 🎯
  • Apply the concept in real projects 🏗️
  • Debug common issues 🐛
  • Write type-safe code ✨

🎯 Introduction

Welcome to this exciting tutorial on TSDoc! 🎉 In this guide, we’ll explore how to document your TypeScript code like a pro using TSDoc, the standard documentation format for TypeScript projects.

You’ll discover how TSDoc can transform your codebase into a well-documented, professional library that other developers will love to use. Whether you’re building internal tools 🛠️, open-source libraries 📚, or enterprise applications 🏢, understanding TSDoc is essential for creating maintainable, self-documenting code.

By the end of this tutorial, you’ll feel confident writing comprehensive documentation that makes your TypeScript code shine! Let’s dive in! 🏊‍♂️

📚 Understanding TSDoc

🤔 What is TSDoc?

TSDoc is like a smart notebook 📓 for your TypeScript code. Think of it as leaving helpful sticky notes 📌 on your code that not only humans can read, but also tools like VS Code can understand and use to provide better IntelliSense!

In TypeScript terms, TSDoc is a standardized way to write documentation comments that get parsed by tools and IDEs. This means you can:

  • ✨ Generate beautiful API documentation automatically
  • 🚀 Get better IntelliSense and autocomplete in VS Code
  • 🛡️ Help other developers (including future you!) understand your code

💡 Why Use TSDoc?

Here’s why developers love TSDoc:

  1. IntelliSense Support 🔒: Your documentation appears in VS Code tooltips
  2. API Documentation 💻: Generate beautiful docs websites automatically
  3. Type Safety 📖: Documentation stays in sync with your code
  4. Team Collaboration 🔧: Everyone understands what code does

Real-world example: Imagine building a payment processing library 💳. With TSDoc, developers using your library can see exactly what each method does, what parameters it expects, and what errors it might throw - all without leaving their editor!

🔧 Basic Syntax and Usage

📝 Simple Example

Let’s start with a friendly example:

// 👋 Basic TSDoc comment structure
/**
 * Calculates the total price including tax
 * @param price - The base price of the item
 * @param taxRate - The tax rate as a decimal (e.g., 0.08 for 8%)
 * @returns The total price including tax
 */
const calculateTotal = (price: number, taxRate: number): number => {
  return price * (1 + taxRate);
};

// 🎨 Documenting a class
/**
 * Represents a shopping cart for an e-commerce application
 * @remarks
 * This class handles all cart operations including adding items,
 * calculating totals, and applying discounts
 */
class ShoppingCart {
  private items: CartItem[] = [];
  
  /**
   * Adds an item to the shopping cart
   * @param item - The item to add to the cart
   * @example
   * ```typescript
   * cart.addItem({ id: "123", name: "TypeScript Book", price: 29.99 });
   * ```
   */
  addItem(item: CartItem): void {
    this.items.push(item);
  }
}

💡 Explanation: Notice how TSDoc comments start with /** and use tags like @param, @returns, and @example to structure the documentation!

🎯 Common TSDoc Tags

Here are the most useful TSDoc tags:

// 🏗️ Common TSDoc tags in action
interface User {
  id: string;
  name: string;
  email: string;
}

/**
 * Creates a new user account
 * 
 * @param userData - The user information
 * @returns A promise that resolves to the created user
 * 
 * @throws {@link ValidationError}
 * Thrown if the email is invalid
 * 
 * @example
 * ```typescript
 * const user = await createUser({
 *   name: "Sarah",
 *   email: "[email protected]"
 * });
 * ```
 * 
 * @see {@link updateUser} for updating existing users
 * @since v1.0.0
 * @deprecated Use {@link createUserV2} instead
 */
async function createUser(userData: Omit<User, "id">): Promise<User> {
  // 🚀 Implementation here
  return { id: "123", ...userData };
}

💡 Practical Examples

🛒 Example 1: E-Commerce Product API

Let’s document a real product management system:

// 🛍️ Product management with comprehensive TSDoc

/**
 * Represents a product in the e-commerce system
 * @public
 */
interface Product {
  /** Unique product identifier */
  id: string;
  /** Product display name */
  name: string;
  /** Price in USD */
  price: number;
  /** Product emoji for fun display! */
  emoji: string;
  /** Stock quantity available */
  stock: number;
}

/**
 * Manages product inventory and operations
 * @remarks
 * This service handles all product-related operations including
 * inventory management, pricing updates, and stock tracking
 * 
 * @example
 * ```typescript
 * const productService = new ProductService();
 * const laptop = await productService.createProduct({
 *   name: "Gaming Laptop",
 *   price: 999.99,
 *   emoji: "💻",
 *   stock: 50
 * });
 * ```
 */
class ProductService {
  private products: Map<string, Product> = new Map();
  
  /**
   * Creates a new product in the inventory
   * 
   * @param productData - The product information without ID
   * @returns The created product with generated ID
   * 
   * @throws {@link Error}
   * Thrown if a product with the same name already exists
   * 
   * @example
   * ```typescript
   * const coffee = await productService.createProduct({
   *   name: "Premium Coffee",
   *   price: 12.99,
   *   emoji: "☕",
   *   stock: 100
   * });
   * console.log(`Created: ${coffee.emoji} ${coffee.name}`);
   * ```
   */
  createProduct(productData: Omit<Product, "id">): Product {
    const id = Date.now().toString();
    const product: Product = { ...productData, id };
    this.products.set(id, product);
    console.log(`✅ Created product: ${product.emoji} ${product.name}`);
    return product;
  }
  
  /**
   * Updates product stock after a purchase
   * 
   * @param productId - The product ID
   * @param quantity - The quantity to deduct from stock
   * @returns The updated product
   * 
   * @throws {@link Error}
   * Thrown if product not found or insufficient stock
   * 
   * @remarks
   * This method is transactional and will rollback on error
   */
  purchaseProduct(productId: string, quantity: number): Product {
    const product = this.products.get(productId);
    if (!product) {
      throw new Error(`❌ Product ${productId} not found`);
    }
    if (product.stock < quantity) {
      throw new Error(`❌ Insufficient stock for ${product.emoji} ${product.name}`);
    }
    
    product.stock -= quantity;
    console.log(`📦 Purchased ${quantity}x ${product.emoji} ${product.name}`);
    return product;
  }
}

🎯 Try it yourself: Add a method to search products by name with proper TSDoc!

🎮 Example 2: Game State Manager

Let’s document a game state management system:

// 🏆 Game state documentation example

/**
 * Represents the current state of a player in the game
 * @public
 */
interface PlayerState {
  /** Player's unique identifier */
  id: string;
  /** Player's display name */
  name: string;
  /** Current score */
  score: number;
  /** Current level (1-100) */
  level: number;
  /** Achievements earned */
  achievements: Achievement[];
}

/**
 * Represents an achievement in the game
 * @public
 */
interface Achievement {
  /** Achievement ID */
  id: string;
  /** Display name */
  name: string;
  /** Achievement emoji */
  emoji: string;
  /** When the achievement was earned */
  earnedAt: Date;
}

/**
 * Manages game state and player progression
 * 
 * @remarks
 * This class handles all game state operations including
 * score tracking, level progression, and achievement unlocking
 * 
 * @example
 * ```typescript
 * const game = new GameStateManager();
 * const player = game.createPlayer("Alice");
 * game.addScore(player.id, 100);
 * ```
 * 
 * @public
 */
class GameStateManager {
  private players: Map<string, PlayerState> = new Map();
  
  /**
   * Creates a new player
   * 
   * @param name - The player's display name
   * @returns The created player state
   * 
   * @example
   * ```typescript
   * const player = game.createPlayer("Bob");
   * console.log(`Welcome ${player.name}! 🎮`);
   * ```
   */
  createPlayer(name: string): PlayerState {
    const player: PlayerState = {
      id: Date.now().toString(),
      name,
      score: 0,
      level: 1,
      achievements: [{ 
        id: "first-steps",
        name: "First Steps",
        emoji: "🌟",
        earnedAt: new Date()
      }]
    };
    
    this.players.set(player.id, player);
    console.log(`🎮 ${name} joined the game!`);
    return player;
  }
  
  /**
   * Adds score to a player and handles level progression
   * 
   * @param playerId - The player's ID
   * @param points - Points to add
   * @returns The updated player state
   * 
   * @throws {@link Error}
   * Thrown if player not found
   * 
   * @fires levelUp - When player reaches a new level
   * 
   * @example
   * ```typescript
   * // Add 50 points to player
   * const updated = game.addScore(player.id, 50);
   * if (updated.level > player.level) {
   *   console.log("Level up! 🎉");
   * }
   * ```
   */
  addScore(playerId: string, points: number): PlayerState {
    const player = this.players.get(playerId);
    if (!player) {
      throw new Error(`❌ Player ${playerId} not found`);
    }
    
    player.score += points;
    const newLevel = Math.floor(player.score / 100) + 1;
    
    if (newLevel > player.level) {
      player.level = newLevel;
      player.achievements.push({
        id: `level-${newLevel}`,
        name: `Level ${newLevel} Master`,
        emoji: "🏆",
        earnedAt: new Date()
      });
      console.log(`🎉 ${player.name} reached level ${newLevel}!`);
    }
    
    return player;
  }
}

🚀 Advanced Concepts

🧙‍♂️ Advanced Topic 1: Generic Type Documentation

When you’re ready to level up, document your generic types:

// 🎯 Advanced generic documentation
/**
 * A generic result type for operations that might fail
 * 
 * @typeParam T - The type of the success value
 * @typeParam E - The type of the error value
 * 
 * @example
 * ```typescript
 * type UserResult = Result<User, ValidationError>;
 * 
 * function getUser(id: string): UserResult {
 *   if (!id) {
 *     return { success: false, error: new ValidationError("Invalid ID") };
 *   }
 *   return { success: true, data: user };
 * }
 * ```
 */
type Result<T, E = Error> = 
  | { success: true; data: T; sparkles: "✨" }
  | { success: false; error: E; sparkles: "💫" };

/**
 * Creates a type-safe event emitter
 * 
 * @typeParam Events - Record of event names to payload types
 * 
 * @example
 * ```typescript
 * interface GameEvents {
 *   levelUp: { player: string; newLevel: number };
 *   achievement: { player: string; achievement: Achievement };
 * }
 * 
 * const emitter = new TypedEventEmitter<GameEvents>();
 * emitter.on("levelUp", ({ player, newLevel }) => {
 *   console.log(`${player} reached level ${newLevel}! 🎉`);
 * });
 * ```
 */
class TypedEventEmitter<Events extends Record<string, any>> {
  /**
   * Subscribes to an event
   * @param event - The event name
   * @param handler - The event handler function
   */
  on<K extends keyof Events>(event: K, handler: (payload: Events[K]) => void): void {
    // 🚀 Implementation
  }
}

🏗️ Advanced Topic 2: Module-Level Documentation

Document entire modules and namespaces:

// 🚀 Module-level documentation
/**
 * @packageDocumentation
 * 
 * ## Payment Processing Module 💳
 * 
 * This module provides a complete payment processing solution
 * with support for multiple payment providers.
 * 
 * ### Features:
 * - ✨ Multiple payment gateway support
 * - 🛡️ PCI-compliant card handling
 * - 🌍 Multi-currency support
 * - 📊 Transaction reporting
 * 
 * ### Quick Start:
 * ```typescript
 * import { PaymentProcessor } from '@myapp/payments';
 * 
 * const processor = new PaymentProcessor({
 *   provider: 'stripe',
 *   apiKey: process.env.STRIPE_KEY
 * });
 * 
 * const result = await processor.charge({
 *   amount: 2999,
 *   currency: 'USD',
 *   card: customerCard
 * });
 * ```
 * 
 * @module payments
 */

/**
 * Main payment processor class
 * @public
 */
export class PaymentProcessor {
  // 💳 Implementation
}

⚠️ Common Pitfalls and Solutions

😱 Pitfall 1: Outdated Documentation

// ❌ Wrong way - documentation doesn't match code!
/**
 * Calculates the sum of two numbers
 * @param a - First number
 * @param b - Second number
 * @returns The sum
 */
const multiply = (a: number, b: number): number => {
  return a * b; // 💥 Documentation says sum, but we're multiplying!
};

// ✅ Correct way - keep docs in sync!
/**
 * Multiplies two numbers together
 * @param a - First number
 * @param b - Second number
 * @returns The product of a and b
 */
const multiply = (a: number, b: number): number => {
  return a * b; // ✅ Documentation matches implementation!
};

🤯 Pitfall 2: Missing Important Details

// ❌ Insufficient documentation
/**
 * Processes payment
 */
async function processPayment(amount: number): Promise<boolean> {
  // 💥 What currency? What errors? What does true/false mean?
  return true;
}

// ✅ Comprehensive documentation
/**
 * Processes a payment transaction
 * 
 * @param amount - The payment amount in cents (USD)
 * @returns `true` if payment successful, `false` if declined
 * 
 * @throws {@link NetworkError}
 * Thrown if payment gateway is unreachable
 * 
 * @throws {@link ValidationError}
 * Thrown if amount is negative or exceeds limits
 * 
 * @example
 * ```typescript
 * try {
 *   const success = await processPayment(2999); // $29.99
 *   if (success) {
 *     console.log("✅ Payment processed!");
 *   }
 * } catch (error) {
 *   console.error("❌ Payment failed:", error);
 * }
 * ```
 */
async function processPayment(amount: number): Promise<boolean> {
  // ✅ Clear implementation
  return true;
}

🛠️ Best Practices

  1. 🎯 Document Public APIs: Focus on public methods and interfaces
  2. 📝 Use Examples: Show real usage with @example tags
  3. 🛡️ Document Errors: Use @throws for all possible exceptions
  4. 🎨 Stay Consistent: Use the same style throughout your project
  5. ✨ Keep It Fresh: Update docs when code changes

🧪 Hands-On Exercise

🎯 Challenge: Document a Task Management System

Create comprehensive TSDoc documentation for this task management system:

📋 Requirements:

  • ✅ Document all interfaces and classes
  • 🏷️ Include examples for each public method
  • 👤 Document type parameters
  • 📅 Include error documentation
  • 🎨 Add module-level documentation

🚀 Bonus Points:

  • Use @see tags to link related methods
  • Add @since tags for versioning
  • Include @remarks for additional context

💡 Solution

🔍 Click to see solution
/**
 * @packageDocumentation
 * 
 * ## Task Management System 📋
 * 
 * A comprehensive task management solution with support for
 * projects, assignments, and deadlines.
 * 
 * @module tasks
 */

/**
 * Priority levels for tasks
 * @public
 */
export type Priority = "low" | "medium" | "high" | "urgent";

/**
 * Task status values
 * @public
 */
export type TaskStatus = "pending" | "in-progress" | "completed" | "cancelled";

/**
 * Represents a task in the system
 * 
 * @remarks
 * Tasks are the fundamental unit of work in the system.
 * Each task belongs to a project and can be assigned to users.
 * 
 * @public
 */
export interface Task {
  /** Unique task identifier */
  id: string;
  /** Task title */
  title: string;
  /** Detailed task description */
  description: string;
  /** Task priority level */
  priority: Priority;
  /** Current task status */
  status: TaskStatus;
  /** Task emoji for visual identification */
  emoji: string;
  /** Optional due date */
  dueDate?: Date;
  /** User ID of assignee */
  assignee?: string;
  /** Project ID this task belongs to */
  projectId: string;
  /** Task creation timestamp */
  createdAt: Date;
  /** Last update timestamp */
  updatedAt: Date;
}

/**
 * Service for managing tasks
 * 
 * @remarks
 * This service provides complete CRUD operations for tasks
 * along with advanced features like filtering and batch operations.
 * 
 * @example
 * ```typescript
 * const taskService = new TaskService();
 * 
 * // Create a new task
 * const task = await taskService.createTask({
 *   title: "Implement TSDoc",
 *   description: "Add documentation to all public APIs",
 *   priority: "high",
 *   emoji: "📝",
 *   projectId: "proj-123"
 * });
 * 
 * // Assign to user
 * await taskService.assignTask(task.id, "user-456");
 * ```
 * 
 * @public
 */
export class TaskService {
  private tasks: Map<string, Task> = new Map();
  
  /**
   * Creates a new task
   * 
   * @param taskData - The task information
   * @returns The created task with generated ID and timestamps
   * 
   * @throws {@link ValidationError}
   * Thrown if required fields are missing or invalid
   * 
   * @example
   * ```typescript
   * const task = await taskService.createTask({
   *   title: "Review PR",
   *   description: "Review the TypeScript refactor PR",
   *   priority: "medium",
   *   emoji: "👀",
   *   projectId: "proj-123",
   *   dueDate: new Date("2024-12-31")
   * });
   * console.log(`Created task: ${task.emoji} ${task.title}`);
   * ```
   * 
   * @since v1.0.0
   */
  async createTask(taskData: Omit<Task, "id" | "status" | "createdAt" | "updatedAt">): Promise<Task> {
    const task: Task = {
      ...taskData,
      id: `task-${Date.now()}`,
      status: "pending",
      createdAt: new Date(),
      updatedAt: new Date()
    };
    
    this.tasks.set(task.id, task);
    console.log(`✅ Created task: ${task.emoji} ${task.title}`);
    return task;
  }
  
  /**
   * Assigns a task to a user
   * 
   * @param taskId - The task ID
   * @param userId - The user ID to assign to
   * @returns The updated task
   * 
   * @throws {@link NotFoundError}
   * Thrown if task doesn't exist
   * 
   * @throws {@link ValidationError}
   * Thrown if task is already completed
   * 
   * @fires taskAssigned - Event emitted when task is assigned
   * 
   * @example
   * ```typescript
   * try {
   *   const updated = await taskService.assignTask("task-123", "user-456");
   *   console.log(`📋 Task assigned to user ${updated.assignee}`);
   * } catch (error) {
   *   console.error("❌ Failed to assign task:", error);
   * }
   * ```
   * 
   * @see {@link unassignTask} to remove assignment
   * @since v1.0.0
   */
  async assignTask(taskId: string, userId: string): Promise<Task> {
    const task = this.tasks.get(taskId);
    if (!task) {
      throw new Error(`❌ Task ${taskId} not found`);
    }
    
    if (task.status === "completed") {
      throw new Error(`❌ Cannot assign completed task`);
    }
    
    task.assignee = userId;
    task.updatedAt = new Date();
    console.log(`👤 Task assigned to ${userId}`);
    return task;
  }
  
  /**
   * Filters tasks by various criteria
   * 
   * @typeParam K - The key to filter by
   * 
   * @param key - The task property to filter by
   * @param value - The value to match
   * @returns Array of matching tasks
   * 
   * @example
   * ```typescript
   * // Get all high priority tasks
   * const urgentTasks = taskService.filterTasks("priority", "high");
   * 
   * // Get tasks for a specific project
   * const projectTasks = taskService.filterTasks("projectId", "proj-123");
   * 
   * // Get assigned tasks
   * const myTasks = taskService.filterTasks("assignee", "user-456");
   * ```
   * 
   * @since v1.1.0
   */
  filterTasks<K extends keyof Task>(key: K, value: Task[K]): Task[] {
    return Array.from(this.tasks.values()).filter(task => task[key] === value);
  }
}

🎓 Key Takeaways

You’ve learned so much! Here’s what you can now do:

  • Write TSDoc comments with confidence 💪
  • Document complex types and generics properly 🛡️
  • Create examples that help other developers 🎯
  • Generate documentation from your code 🐛
  • Build professional TypeScript libraries! 🚀

Remember: Good documentation is a gift to your future self and your teammates! 🤝

🤝 Next Steps

Congratulations! 🎉 You’ve mastered TSDoc documentation!

Here’s what to do next:

  1. 💻 Document your current TypeScript project
  2. 🏗️ Set up automated documentation generation
  3. 📚 Move on to our next tutorial: TypeScript ESLint Rules
  4. 🌟 Share your well-documented code with the world!

Remember: Every well-documented codebase started with a single TSDoc comment. Keep documenting, keep learning, and most importantly, have fun! 🚀


Happy coding! 🎉🚀✨