← Blog
Apr 15, 2024

TypeScript for AI Development: Type Safety Meets LLMs

Why TypeScript is our primary language for AI applications. Type safety, better tooling, and patterns that prevent common mistakes.

typescriptdevelopmentAIpatternstooling

AI applications handle complex, dynamic data. Type safety prevents entire categories of bugs.

This is why 60% of our codebase is TypeScript.

The Case for Types

Before TypeScript

async function processMessage(message) {
  const result = await ai.complete(message.content);
  return result.text; // What if result has no text?
}

After TypeScript

interface Message {
  role: 'user' | 'assistant' | 'system';
  content: string;
}

interface AIResponse {
  text: string;
  tokens: number;
  finishReason: 'stop' | 'length' | 'error';
}

async function processMessage(message: Message): Promise<AIResponse> {
  const result = await ai.complete(message.content);
  return result; // Compiler ensures shape
}

Essential Patterns

Discriminated Unions for AI Responses

type AIResult = 
  | { success: true; text: string; tokens: number }
  | { success: false; error: string; retryable: boolean };

function handleResult(result: AIResult) {
  if (result.success) {
    console.log(result.text); // TypeScript knows text exists
  } else {
    console.error(result.error); // TypeScript knows error exists
  }
}

Branded Types for Safety

type TokenCount = number & { readonly brand: unique symbol };
type UserId = string & { readonly brand: unique symbol };

function countTokens(text: string): TokenCount {
  return encode(text).length as TokenCount;
}

// Prevents mixing up different number/string types
function processUser(userId: UserId, tokens: TokenCount) { }

Zod for Runtime Validation

import { z } from 'zod';

const AIResponseSchema = z.object({
  text: z.string(),
  tokens: z.number().int().positive(),
  model: z.enum(['gpt-4', 'claude-3', 'llama-3'])
});

type AIResponse = z.infer<typeof AIResponseSchema>;

async function safeApiCall(): Promise<AIResponse> {
  const raw = await fetch('/api/ai').then(r => r.json());
  return AIResponseSchema.parse(raw); // Throws if invalid
}

Productivity Gains

Metric JavaScript TypeScript
Bug discovery Runtime Compile time
Refactoring Scary Confident
Documentation Manual Automatic
IDE support Basic Excellent
API integration Error-prone Type-safe

Our Configuration

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,
    "exactOptionalPropertyTypes": true,
    "moduleResolution": "bundler",
    "target": "ES2022"
  }
}

Strict mode catches the bugs that matter.


TypeScript is foundational to our development. See Bun vs Node for our runtime choice.

All posts Work with me