TypeScript: Friend or Foe?
Many vibe coders avoid TypeScript. It adds friction. The AI occasionally generates code with type errors. Fixing type errors feels like fighting the tools instead of building the product.
But this is a false economy. TypeScript catches a specific category of bugs — type errors — before they reach production. And in an AI-assisted workflow where the AI is generating significant portions of your code, type errors are more common than in hand-written code.
The key is using TypeScript at the right level of strictness for your project phase.
---
The Two-Phase TypeScript Strategy
Phase 1 (Building fast, first version): Use TypeScript but configure it loosely. Focus on getting the product working.
Phase 2 (Stabilising for production): Tighten type safety on the paths that matter most — auth, payments, API routes.
---
The Types That Pay the Biggest Dividend
1. API Response Types
Define the exact shape of what your API routes return. Every component that consumes your API benefits:
```typescript
// types/api.ts
export type ApiResponse
// Usage in API route:
export async function GET(): Promise
Get advanced blueprint resources
Enter your email to receive technical blueprints, checklists, and visual configurations accompanying this chapter.
No spam. Unsubscribe any time.
return Response.json({ data, error: null } satisfies ApiResponse
// Usage in client component — TypeScript knows exactly what you get:
const { data: posts, error } = await fetchFromApiGenerate TypeScript types from your live Supabase database
npx supabase gen types typescript --project-id your-project-id > types/database.types.ts
typescript
import { createClient } from '@supabase/supabase-js';
import type { Database } from '@/types/database.types';
const supabase = createClient
// Now fully typed — TypeScript knows exactly what columns exist const { data: posts } = await supabase .from('posts') .select('id, title, status, created_at') .eq('status', 'published'); // TypeScript validates 'status' is a valid column // posts is { id: string; title: string; status: string; created_at: string }[] | null
typescript import { z } from 'zod';const CreatePostSchema = z.object({ title: z.string().min(3).max(200), content: z.string().min(10), status: z.enum(['draft', 'published']), });
// TypeScript infers the type automatically from the schema
type CreatePostInput = z.infer
// In your API route: export async function POST(request: Request) { const body = await request.json(); const parsed = CreatePostSchema.safeParse(body); if (!parsed.success) { return Response.json({ error: parsed.error.flatten() }, { status: 400 }); } // parsed.data is now fully typed as CreatePostInput const post = await createPost(parsed.data); return Response.json({ data: post }); }
Write a TypeScript function that [does X].Types required:
- Use this existing type for input: [paste type]
- Return type should be: Promise
> using our ApiResponse generic - Do not use 'any' — use 'unknown' with type narrowing if needed
- Include proper error handling with typed error returns
When you give the AI your existing types as context, it generates code that integrates cleanly with your existing type system rather than inventing its own.