All Courses

Advanced TypeScript for Harness Builders

Advanced TypeScript for Harness Builders

Learn advanced TypeScript by studying production code from the pi-mono codebase — types that stand up to real agent-harness workloads.

Advanced English 27 chapters

typescript   type-system   generics   async   agent-harness

A comprehensive course built from real-world patterns in the pi-mono codebase. Designed for developers who know JavaScript well and want to learn TypeScript by studying production code that powers AI agent harnesses.

Each chapter is a focused micro-lesson with links to the exact line in the pi-mono source. Videos for each chapter will be added as the course grows.

Part 0 — Bridge: JavaScript to TypeScript

0a. Type Annotations & Basic Types

How TypeScript annotates parameters, return types, and variables — the gap that separates JS from TS.

In JavaScript, types are implicit. In TypeScript, you annotate parameters, return types, and variables.

Function parameters and return types:

File: packages/mom/src/tools/read.ts L22-25

// JS: function isImageFile(filePath) { ... }
// TS: parameter type and return type are explicit
function isImageFile(filePath: string): string | null {
  const ext = extname(filePath).toLowerCase();
  return IMAGE_MIME_TYPES[ext] || null;
}
  • filePath: string — parameter annotation
  • : string | null — return type (can return a string OR null)

Variable annotations:

File: packages/mom/src/main.ts L115-118

let messageTs: string | null = null;
const threadMessageTs: string[] = [];
let accumulatedText = ""; // TS infers: string (no annotation needed)
let isWorking = true; // TS infers: boolean

Key insight: You don’t always need annotations. TypeScript infers types from initial values. Annotate when:

  • A function parameter has no default
  • The type is ambiguous (e.g., null initial value)
  • You want to be explicit for readability

Async functions with Promise return types:

File: packages/mom/src/sandbox.ts L51-66

function execSimple(cmd: string, args: string[]): Promise<string> {
  return new Promise((resolve, reject) => {
    const child = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
    let stdout = "";
    child.stdout?.on("data", (d) => {
      stdout += d;
    });
    child.on("close", (code) => {
      if (code === 0) resolve(stdout);
      else reject(new Error(`Exit code ${code}`));
    });
  });
}

Promise<string> — the function returns a Promise that resolves to a string.

void return type:

File: packages/mom/src/log.ts L72-73

export function logUserMessage(ctx: LogContext, text: string): void {
  console.log(chalk.green(`${timestamp()} ${formatContext(ctx)} ${text}`));
}

void means “this function doesn’t return anything useful.” In JS you’d just omit the return statement; in TS you declare that intent.