Skip to content

Vibe Coding with TypeScript — A Quality-First Framework

Vibe coding ships features fast, but AI code has 1.7x more defects. This TypeScript framework adds quality gates that catch AI errors before production.

· · 6 min read
Developer writing code on a laptop with multiple files open and terminal visible

Quick Take

Vibe coding is real productivity, until you're debugging a production crash from code you never fully read. Four TypeScript quality gates take under 30 minutes to configure and catch the 94% of AI errors that are type-related before they reach your users.

Andrej Karpathy posted the term in February 2025: "There's a new kind of coding I call 'vibe coding', where you fully give in to the vibes, embrace exponentials, and forget that the code even exists." (source) Forty thousand retweets. Every developer recognized themselves in it.

Vibe coding isn't reckless. It's what happens when AI tools get good enough that you can describe intent and review output instead of writing every line. I've shipped production features in two hours that would have taken two days with Cursor. That speed is real, and I don't want to give it back.

But here's what I can't stop thinking about: CodeScene's 2026 research found that AI-generated code has 1.7x more defects than human-written code. Vibe-coded projects accumulate technical debt 3x faster. And 94% of those AI errors are type-related.

That last number changes the conversation. If nearly all AI code quality failures are type errors, and TypeScript exists specifically to catch type errors at compile time, then TypeScript isn't a nicety for vibe coding. It's the only safety net that makes it sustainable.

TL;DR:

  • AI code has 1.7x more defects; 94% are type-related (CodeScene 2026)
  • TypeScript strict: true catches most of these at compile time, not at runtime
  • Define types before prompting AI, better output AND automatic validation
  • The full framework configures in under 30 minutes

Why Pure Vibe Coding Fails at Scale

When you ask an AI to "add a user dashboard," it writes plausible-looking code fast. That's the problem. "Plausible" and "correct" aren't the same thing. AI models predict what tokens should follow, they don't execute your code mentally and verify the logic.

The defects follow predictable patterns. I've seen the same ones dozens of times across different projects:

  • Functions typed as returning any when the actual shape matters downstream
  • API responses wrapped in Record<string, unknown> instead of a real interface
  • Optional chaining skipped: user.profile.name instead of user.profile?.name
  • Array access assumed safe: items[0].id on a list that might be empty

None of these fail in development. All of them crash in production, triggered by the exact user who hits the edge case. Sound familiar?

TypeScript with strict: true catches every single pattern above at compile time, before a single line reaches your users.

A monitor showing colorful fluid artwork on a neon-lit gaming desk
Photo by Jack B on Unsplash

The Quality-First Framework

Step 1: Types Before Prompts

Don't ask AI to "write a function that fetches user orders." Instead, define the shape of the data first:

interface Order {
  id: string;
  userId: string;
  items: OrderItem[];
  status: 'pending' | 'shipped' | 'delivered' | 'cancelled';
  total: number;
  createdAt: Date;
}

interface FetchOrdersResult {
  orders: Order[];
  nextCursor: string | null;
  total: number;
}

Now prompt with the contract attached: "Write a fetchOrders(userId: string, cursor?: string): Promise<FetchOrdersResult> function using these types."

Two things happen. The AI has a precise contract rather than guessing the return shape. And when it generates the implementation, TypeScript checks that the output actually satisfies FetchOrdersResult. Wrong shape? Compile error immediately, not a silent crash six hours later in production.

Step 2: The Minimum tsconfig

Your tsconfig.json for any vibe coding project:

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

strict: true is non-negotiable, it bundles eight flags that together catch the most common AI errors. noUncheckedIndexedAccess makes array indexing return T | undefined, so items[0] forces you to handle the empty case rather than assume. exactOptionalPropertyTypes stops AI from assigning undefined to optional properties that don't accept it.

The full breakdown of every strict flag is in the TypeScript strict mode guide.

Step 3: typescript-eslint Catches What tsc Misses

The TypeScript compiler and ESLint with typescript-eslint have different coverage. The compiler checks types. ESLint catches patterns that are technically valid TypeScript but almost always indicate AI-generated bugs:

npm install -D @typescript-eslint/eslint-plugin @typescript-eslint/parser

Add to your ESLint config:

{
  "extends": ["plugin:@typescript-eslint/strict-type-checked"],
  "parserOptions": {
    "project": true
  },
  "rules": {
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-non-null-assertion": "error",
    "@typescript-eslint/no-unsafe-assignment": "error"
  }
}

These three rules block any, the ! non-null assertion operator, and unsafe assignments. AI assistants reach for all three constantly when they can't infer an exact type. Treating them as errors forces the AI (or you in the review) to be explicit.

Step 4: Zod at Runtime Boundaries

TypeScript disappears at runtime. External API responses can return anything regardless of what your types say. The one gap in TypeScript's protection is the boundary where external data enters your system, fetch responses, localStorage reads, URL params.

Zod closes that gap:

import { z } from 'zod';

const OrderSchema = z.object({
  id: z.string(),
  userId: z.string(),
  items: z.array(z.object({
    productId: z.string(),
    quantity: z.number().positive(),
    price: z.number()
  })),
  status: z.enum(['pending', 'shipped', 'delivered', 'cancelled']),
  total: z.number()
});

type Order = z.infer<typeof OrderSchema>; // TypeScript type from schema, free

const response = await fetch('/api/orders/123');
const data = OrderSchema.parse(await response.json());
// data is fully typed AND validated, bad shapes crash loudly at the boundary

You get full TypeScript inference for free via z.infer, and any malformed API response fails loudly at the entry point rather than corrupting data silently three function calls deep.

A bedroom coding corner bathed in purple and pink neon light with a HELLO sign
Photo by Chuck Fortner on Unsplash

What This Actually Gives You

Write types first, generate code second, let TypeScript and ESLint check the output. That's the complete framework.

Is it slower than pure vibe coding? No. I've measured it. The type definitions take 5-10 minutes. The CI check on push takes 30 seconds. What you skip is the production debugging session two weeks later that costs a full day. The friction moves forward, closer to where you can actually fix things cheaply.

The real win is compounding. Each project you build with this framework produces reusable type definitions that speed up the next project. AI generates better code when you feed it well-typed context. And you build the habit of thinking about data shapes before implementations, which makes you a better reviewer of AI output, not just a faster shipper.

For the deeper connection between TypeScript types and AI assistant quality, how well-typed context measurably improves Copilot and Claude Code output, see TypeScript types for AI.

Frequently Asked Questions

What is vibe coding?
Vibe coding is a development style coined by Andrej Karpathy in February 2025 where you describe intent to an AI assistant, review the output briefly, and ship it without deeply understanding every line. The term spread because it accurately describes how most developers already use Copilot or Claude Code. The productivity gains are real, but so is the quality risk when there are no guardrails.
Why does TypeScript improve AI-generated code quality?
94% of errors in AI-generated code are type-related according to CodeScene's 2026 research. TypeScript strict mode catches these at compile time rather than at runtime in production. When you define types before prompting AI, you hand the model a precise contract it must satisfy, and TypeScript enforces that the generated code actually satisfies it.
What TypeScript settings work best for vibe coding projects?
Start with strict: true as the baseline, then add noUncheckedIndexedAccess and exactOptionalPropertyTypes. For linting, typescript-eslint with the strictTypeChecked ruleset flags unsafe type assertions and non-null assertions that AI commonly produces. These settings together cover the majority of type bugs introduced by AI code generation.
Does this quality framework slow down vibe coding?
No, it shifts friction earlier. You spend 5-10 minutes defining types upfront instead of 30-60 minutes debugging a type error in production. TypeScript errors from AI-generated code are also far easier to fix than runtime bugs because the type checker tells you exactly what is wrong and where. You ship just as fast, but you stop fixing surprises a week later.