Skip to content
Engineering May 20, 2026

Why we use TypeScript for every AI integration we build

Untyped LLM responses are a liability. Here is how we handle the contract boundary.

LLM responses are untyped by default. The model returns a string. What you do with that string is your problem. In production AI systems, “your problem” becomes a runtime error at 2am, a corrupted record, or a silent failure that compounds over a thousand requests before anyone notices.

TypeScript does not solve this automatically — but it forces you to be explicit about where the contract boundary is. When we receive an LLM response, we parse and validate it at the edge. The rest of the system works with typed data. The untyped zone is as small as we can make it.

The pattern

Every LLM call in our systems has three layers: the prompt template, the response parser, and the typed output. The parser is a Zod schema (or equivalent) that validates the raw response against the expected shape. If it fails, we catch it at the boundary — not three steps later when the data hits a database write.

This sounds obvious. Most teams skip it anyway because it feels like overhead when you are moving fast. It is not overhead. It is the thing that makes the system maintainable six months after you shipped it.

Structured output changes the calculation

OpenAI’s structured output mode and Anthropic’s tool_use pattern both help — but they do not eliminate the validation layer. Structured outputs reduce schema violations; they do not prevent semantic ones. The model can return valid JSON that is logically incorrect for your use case. Type safety and domain validation are two different problems.

We use TypeScript because it makes the second problem harder to ignore. When you have to write the type, you have to think about what you are actually expecting. That thinking is the work.