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
Ever built an amazing TypeScript library but struggled to document it properly? 📚 Or maybe you’ve worked with libraries that had confusing or missing documentation? Well, today we’re diving into TypeDoc - the TypeScript documentation superhero that turns your code comments into beautiful, searchable documentation! 🦸♂️
Think of TypeDoc as your personal documentation assistant that reads your TypeScript code and creates professional API documentation automatically. It’s like having a technical writer who never sleeps! 💤
📚 Understanding TypeDoc
TypeDoc is like a smart translator that takes your TypeScript code and JSDoc comments and transforms them into stunning HTML documentation. Imagine you’re writing a cookbook 📖 - TypeDoc takes your recipes (code) and your cooking notes (comments) and creates a beautiful, searchable cookbook website!
Here’s what makes TypeDoc special:
- 🔍 Automatic Type Extraction: Reads TypeScript types without extra effort
- 🎨 Beautiful Themes: Professional-looking documentation out of the box
- 🔗 Cross-References: Automatically links related types and functions
- 📱 Mobile Friendly: Documentation that works everywhere
🔧 Basic Syntax and Usage
Let’s start by installing TypeDoc and creating our first documented function:
# Install TypeDoc as a dev dependency
npm install --save-dev typedoc
Now, let’s write some documented code:
/**
* 🍕 Represents a delicious pizza order
* @example
* ```typescript
* const myPizza = new Pizza("Margherita", "large");
* console.log(myPizza.getDescription()); // "A large Margherita pizza"
* ```
*/
export class Pizza {
/**
* Creates a new pizza instance
* @param type - The type of pizza (e.g., "Margherita", "Pepperoni")
* @param size - The size of the pizza
*/
constructor(
public type: string,
public size: "small" | "medium" | "large"
) {}
/**
* Gets a friendly description of the pizza
* @returns A human-readable pizza description
*/
getDescription(): string {
return `A ${this.size} ${this.type} pizza`; // 🎉 Delicious!
}
}
/**
* 💰 Calculates the price of a pizza based on size
* @param size - The pizza size
* @returns The price in dollars
* @throws {Error} If the size is not recognized
*/
export function calculatePrice(size: Pizza["size"]): number {
const prices = {
small: 8.99,
medium: 12.99,
large: 16.99
};
if (!prices[size]) {
throw new Error(`Unknown size: ${size}`); // 😱 Oops!
}
return prices[size];
}
To generate documentation:
# Generate documentation
npx typedoc src/index.ts
# With custom options
npx typedoc --out docs src --name "My Pizza API"
💡 Practical Examples
Example 1: E-commerce Product API 🛒
Let’s document a shopping cart system:
/**
* 🛍️ Represents a product in our online store
* @category Models
*/
export interface Product {
/** Unique product identifier */
id: string;
/** Product display name */
name: string;
/** Price in cents (to avoid floating point issues) */
priceInCents: number;
/** Whether the product is currently in stock */
inStock: boolean;
/** Product categories for filtering */
categories: string[];
}
/**
* 🛒 Shopping cart management service
* @remarks
* This service handles all cart operations including adding,
* removing, and calculating totals.
*
* @example
* ```typescript
* const cart = new ShoppingCart();
* cart.addItem({ id: "123", name: "TypeScript Book", priceInCents: 2999, inStock: true, categories: ["books"] });
* console.log(cart.getTotal()); // 29.99
* ```
*/
export class ShoppingCart {
private items: Map<string, { product: Product; quantity: number }> = new Map();
/**
* Adds a product to the cart
* @param product - The product to add
* @param quantity - Number of items to add (default: 1)
* @returns The new quantity of this product in the cart
* @throws {Error} If product is out of stock
* @see {@link removeItem} for removing products
*/
addItem(product: Product, quantity: number = 1): number {
if (!product.inStock) {
throw new Error(`Sorry, ${product.name} is out of stock! 😢`);
}
const existing = this.items.get(product.id);
const newQuantity = (existing?.quantity || 0) + quantity;
this.items.set(product.id, { product, quantity: newQuantity });
return newQuantity; // 🎉 Successfully added!
}
/**
* Calculates the total price of all items
* @returns Total price in dollars
* @public
*/
getTotal(): number {
let total = 0;
this.items.forEach(({ product, quantity }) => {
total += (product.priceInCents * quantity) / 100;
});
return total; // 💰 Ka-ching!
}
}
Example 2: Game Engine Documentation 🎮
/**
* 🎮 Main game engine for 2D games
* @module GameEngine
*/
/**
* Represents a 2D position in the game world
* @typedef {Object} Position
* @property {number} x - The X coordinate
* @property {number} y - The Y coordinate
*/
export interface Position {
x: number;
y: number;
}
/**
* 👾 Base class for all game entities
* @abstract
* @category Core
*/
export abstract class GameObject {
/**
* @param initialPosition - Starting position in the game world
* @param sprite - Path to the sprite image
*/
constructor(
protected position: Position,
protected sprite: string
) {}
/**
* Updates the game object state
* @param deltaTime - Time elapsed since last update (in milliseconds)
* @abstract
*/
abstract update(deltaTime: number): void;
/**
* Renders the game object to the canvas
* @param context - The 2D rendering context
* @virtual
*/
render(context: CanvasRenderingContext2D): void {
// Default rendering logic here 🎨
}
}
/**
* 🚀 Player-controlled spaceship
* @extends GameObject
* @example
* ```typescript
* const player = new Player({ x: 100, y: 200 }, "spaceship.png");
* player.boost(); // Zoom! 🚀
* ```
*/
export class Player extends GameObject {
private velocity = { x: 0, y: 0 };
private health = 100;
/**
* Activates the spaceship's boost
* @fires Player#boost
* @returns {boolean} Whether boost was successful
*/
boost(): boolean {
if (this.health > 20) {
this.velocity.x *= 2;
this.velocity.y *= 2;
return true; // 🚀 Boost activated!
}
return false; // 😓 Not enough health
}
update(deltaTime: number): void {
this.position.x += this.velocity.x * deltaTime;
this.position.y += this.velocity.y * deltaTime;
}
}
Example 3: REST API Client Documentation 🌐
/**
* 🌐 HTTP client for our REST API
* @packageDocumentation
*/
/**
* API response wrapper
* @typeParam T - The type of data in the response
*/
export interface ApiResponse<T> {
/** Response data */
data: T;
/** HTTP status code */
status: number;
/** Response headers */
headers: Record<string, string>;
}
/**
* Configuration options for the API client
* @public
*/
export interface ApiClientConfig {
/** Base URL for all requests */
baseUrl: string;
/** Default timeout in milliseconds */
timeout?: number;
/** Authentication token */
authToken?: string;
}
/**
* 🔌 Main API client for interacting with our backend
* @remarks
* This client provides type-safe methods for all API endpoints.
*
* @example Basic usage
* ```typescript
* const client = new ApiClient({ baseUrl: "https://api.example.com" });
* const users = await client.getUsers();
* ```
*
* @example With authentication
* ```typescript
* const client = new ApiClient({
* baseUrl: "https://api.example.com",
* authToken: "secret-token"
* });
* ```
*/
export class ApiClient {
private config: Required<ApiClientConfig>;
constructor(config: ApiClientConfig) {
this.config = {
timeout: 5000,
authToken: "",
...config
};
}
/**
* Fetches all users
* @returns Promise resolving to user array
* @throws {NetworkError} If the request fails
* @since 1.0.0
*/
async getUsers(): Promise<ApiResponse<User[]>> {
// Implementation here 🎉
return this.request("/users");
}
/**
* Creates a new user
* @param userData - The user data to create
* @returns The created user
* @beta
*/
async createUser(userData: Omit<User, "id">): Promise<ApiResponse<User>> {
return this.request("/users", {
method: "POST",
body: JSON.stringify(userData)
});
}
/**
* Internal request method
* @internal
*/
private async request<T>(endpoint: string, options?: RequestInit): Promise<ApiResponse<T>> {
// Secret implementation details 🤫
throw new Error("Implementation needed");
}
}
interface User {
id: string;
name: string;
email: string;
}
🚀 Advanced Concepts
Custom TypeDoc Configuration
Create a typedoc.json
file for advanced settings:
{
"entryPoints": ["src/index.ts"],
"out": "docs",
"name": "My Awesome API",
"theme": "default",
"includeVersion": true,
"excludePrivate": true,
"excludeProtected": false,
"readme": "README.md",
"categorizeByGroup": true,
"categoryOrder": ["Core", "Models", "Utils", "*"],
"plugin": ["typedoc-plugin-markdown"],
"githubPages": true,
"navigationLinks": {
"GitHub": "https://github.com/myuser/myproject",
"NPM": "https://npmjs.com/package/mypackage"
}
}
Advanced Documentation Tags
/**
* 🏗️ Advanced builder pattern implementation
* @typeParam T - The type being built
* @since 2.0.0
* @deprecated Use {@link FluentBuilder} instead
* @see {@link https://example.com/docs} for more info
* @todo Add validation support
* @internal This is internal API
* @beta This API is in beta
* @alpha This API is experimental
* @public This is public API
* @readonly This property is read-only
* @override This method overrides the parent
* @virtual This method can be overridden
* @sealed This class cannot be extended
* @event Emitted when something happens
* @fires MyClass#myevent
* @listens MyClass#myevent
* @mixes MyMixin
* @namespace MyNamespace
* @memberof MyClass
* @static This is a static member
* @instance This is an instance member
* @global This is globally available
* @ignore Don't document this
* @inheritdoc Inherit parent documentation
*/
export class AdvancedBuilder<T> {
// Implementation
}
Custom Themes and Plugins
// Using TypeDoc programmatically
import { Application, TSConfigReader, TypeDocReader } from "typedoc";
async function generateDocs() {
const app = new Application();
// Read config files
app.options.addReader(new TSConfigReader());
app.options.addReader(new TypeDocReader());
app.bootstrap({
entryPoints: ["src/index.ts"],
theme: "my-custom-theme",
plugin: ["typedoc-plugin-mermaid"],
});
const project = app.convert();
if (project) {
await app.generateDocs(project, "docs");
console.log("📚 Documentation generated successfully!");
}
}
⚠️ Common Pitfalls and Solutions
❌ Wrong: Missing JSDoc Comments
// No documentation! 😱
export function calculateTax(price: number): number {
return price * 0.08;
}
✅ Correct: Properly Documented
/**
* 💰 Calculates sales tax for a given price
* @param price - The pre-tax price in dollars
* @returns The tax amount in dollars
* @example
* ```typescript
* const tax = calculateTax(100); // Returns 8
* ```
*/
export function calculateTax(price: number): number {
return price * 0.08;
}
❌ Wrong: Documenting Private Implementation Details
export class UserService {
/**
* Secret database connection string
* @private
*/
private dbConnection = "mongodb://secret"; // 🚫 Don't expose secrets!
}
✅ Correct: Document Public API Only
export class UserService {
/** @internal */
private dbConnection = process.env.DB_URL;
/**
* Fetches a user by ID
* @param id - The user's unique identifier
* @returns The user object or null if not found
*/
async getUser(id: string): Promise<User | null> {
// Implementation
}
}
🛠️ Best Practices
- 📝 Document as You Code: Write documentation while the code is fresh in your mind
- 🎯 Focus on “Why”, Not “What”: The code shows what, documentation explains why
- 📚 Use Examples Liberally: Show real usage, not just theory
- 🔗 Cross-Reference Related Items: Use
@see
tags to connect related functionality - 📦 Group Related Items: Use
@category
tags to organize documentation - 🎨 Keep It Consistent: Use the same terminology throughout
- 🚀 Document Edge Cases: Explain what happens in unusual scenarios
Example of great documentation:
/**
* 🔐 Validates user passwords according to security requirements
*
* @remarks
* This function enforces our security policy:
* - Minimum 8 characters
* - At least one uppercase letter
* - At least one number
* - At least one special character
*
* @param password - The password to validate
* @returns `true` if valid, `false` otherwise
*
* @example Valid passwords
* ```typescript
* validatePassword("SecureP@ss1"); // true ✅
* validatePassword("MyStr0ng!Pass"); // true ✅
* ```
*
* @example Invalid passwords
* ```typescript
* validatePassword("weak"); // false ❌ (too short)
* validatePassword("NoNumbers!"); // false ❌ (no digits)
* validatePassword("nocaps123!"); // false ❌ (no uppercase)
* ```
*
* @see {@link hashPassword} for storing passwords
* @see {@link checkPasswordStrength} for strength scoring
* @since 1.2.0
*/
export function validatePassword(password: string): boolean {
const minLength = 8;
const hasUpperCase = /[A-Z]/.test(password);
const hasNumber = /\d/.test(password);
const hasSpecialChar = /[!@#$%^&*]/.test(password);
return password.length >= minLength &&
hasUpperCase &&
hasNumber &&
hasSpecialChar;
}
🧪 Hands-On Exercise
Create a documented TypeScript library for a todo list application! 📝
Your Challenge: Document the following todo list API with TypeDoc:
// Your task: Add comprehensive TypeDoc documentation!
export interface Todo {
id: string;
title: string;
completed: boolean;
createdAt: Date;
tags?: string[];
}
export class TodoList {
private todos: Map<string, Todo> = new Map();
addTodo(title: string, tags?: string[]): Todo {
const todo: Todo = {
id: Date.now().toString(),
title,
completed: false,
createdAt: new Date(),
tags
};
this.todos.set(todo.id, todo);
return todo;
}
completeTodo(id: string): boolean {
const todo = this.todos.get(id);
if (todo) {
todo.completed = true;
return true;
}
return false;
}
getTodosByTag(tag: string): Todo[] {
return Array.from(this.todos.values())
.filter(todo => todo.tags?.includes(tag));
}
getStats() {
const todos = Array.from(this.todos.values());
return {
total: todos.length,
completed: todos.filter(t => t.completed).length,
pending: todos.filter(t => !t.completed).length
};
}
}
💡 Click here for the solution
/**
* 📝 Represents a single todo item
* @public
*/
export interface Todo {
/** Unique identifier for the todo */
id: string;
/** The todo item description */
title: string;
/** Whether the todo has been completed */
completed: boolean;
/** When the todo was created */
createdAt: Date;
/** Optional tags for categorization */
tags?: string[];
}
/**
* 📋 Todo list management system
*
* @remarks
* This class provides a complete todo list implementation with
* support for tags, completion tracking, and statistics.
*
* @example Basic usage
* ```typescript
* const list = new TodoList();
* const todo = list.addTodo("Learn TypeDoc", ["documentation", "typescript"]);
* list.completeTodo(todo.id);
* console.log(list.getStats()); // { total: 1, completed: 1, pending: 0 }
* ```
*
* @public
*/
export class TodoList {
/** @internal */
private todos: Map<string, Todo> = new Map();
/**
* 🆕 Adds a new todo item to the list
*
* @param title - The todo description
* @param tags - Optional array of tags for categorization
* @returns The newly created todo item
*
* @example
* ```typescript
* const todo = list.addTodo("Write documentation", ["urgent", "work"]);
* console.log(todo.id); // "1234567890"
* ```
*/
addTodo(title: string, tags?: string[]): Todo {
const todo: Todo = {
id: Date.now().toString(),
title,
completed: false,
createdAt: new Date(),
tags
};
this.todos.set(todo.id, todo);
return todo; // 🎉 Todo created!
}
/**
* ✅ Marks a todo as completed
*
* @param id - The todo's unique identifier
* @returns `true` if the todo was found and completed, `false` otherwise
*
* @example
* ```typescript
* const success = list.completeTodo("123");
* if (success) {
* console.log("Todo completed! 🎉");
* }
* ```
*/
completeTodo(id: string): boolean {
const todo = this.todos.get(id);
if (todo) {
todo.completed = true;
return true; // ✅ Success!
}
return false; // 😢 Todo not found
}
/**
* 🏷️ Retrieves all todos with a specific tag
*
* @param tag - The tag to filter by
* @returns Array of todos that contain the specified tag
*
* @example
* ```typescript
* const urgentTodos = list.getTodosByTag("urgent");
* console.log(`You have ${urgentTodos.length} urgent todos!`);
* ```
*
* @see {@link Todo.tags} for tag structure
*/
getTodosByTag(tag: string): Todo[] {
return Array.from(this.todos.values())
.filter(todo => todo.tags?.includes(tag));
}
/**
* 📊 Gets statistics about the todo list
*
* @returns Object containing total, completed, and pending counts
*
* @example
* ```typescript
* const stats = list.getStats();
* console.log(`Progress: ${stats.completed}/${stats.total} completed`);
* ```
*/
getStats() {
const todos = Array.from(this.todos.values());
return {
/** Total number of todos */
total: todos.length,
/** Number of completed todos */
completed: todos.filter(t => t.completed).length,
/** Number of pending todos */
pending: todos.filter(t => !t.completed).length
};
}
}
🎓 Key Takeaways
You’ve mastered TypeDoc! Here’s what you’ve learned:
- 📚 Documentation is Code: Treat documentation with the same care as your code
- 🎨 TypeDoc Magic: Automatic beautiful documentation from TypeScript
- 📝 JSDoc Powers: Rich documentation with examples, links, and more
- 🏗️ Structure Matters: Organize with categories, groups, and navigation
- 🚀 Go Beyond Basics: Custom themes, plugins, and programmatic usage
Remember: Great documentation makes your code accessible to everyone! 🌟
🤝 Next Steps
Ready to document like a pro? Here’s what to explore next:
- 🎨 Custom Themes: Create your own TypeDoc theme
- 🔌 Plugin Development: Build TypeDoc plugins
- 🤖 CI/CD Integration: Auto-generate docs on every commit
- 📊 Coverage Reports: Track documentation coverage
- 🌐 API Versioning: Document multiple API versions
Keep documenting, and remember - the best code is well-documented code! Happy coding! 🎉✨
P.S. Your future self (and your teammates) will thank you for writing great documentation! 🙏