Best Model for TypeScript: Let the Compiler Do the Grading
TypeScript gives you a free verifier, which changes which model you should pay for. How to build the tsc loop and where models still fall down.
TypeScript changes the model-selection question in a way most teams do not exploit. You have a fast, deterministic verifier that runs in seconds and rejects a large fraction of what a model gets wrong. That should push you towards a cheaper model that iterates, not a more expensive one that gets it right first time.
Most teams do the opposite. They pay for a frontier model, accept its first output, and let the type errors surface in someone's editor an hour later. The compiler is sitting right there and nothing in the loop is asking it.
Put tsc in the loop, not after it
The mechanism is simple and it works with any model. Generate the code, write it to disk, run the type checker, and if it fails, feed the errors back and ask for a correction. Repeat up to a small cap.
TypeScript errors are unusually good feedback for a model. They name the file, the line, the expected type and the received type. That is far more actionable than a runtime stack trace, and models correct against them reliably — most type errors are resolved on the first retry.
The economics follow from that. If two iterations of a cheap model reach a clean compile, the total cost is often lower than one call to a model five times the price, and the outcome is identical because both end at zero type errors. When a cheap model is enough covers the general form of this argument.
Strict mode is what makes the loop worth having
The value of the verifier depends entirely on how strict it is. With loose settings, the checker accepts most of what a model produces and the loop teaches you nothing.
Turn on strict null checks and the checker starts catching the single most common category of generated bug: a value that might be undefined being used as though it cannot be. Turn on no-implicit-any and it stops the model quietly opting out of type safety when it is unsure.
The last one matters more than it sounds. A model that does not know a type will reach for any, and every any is a hole in the verifier that will not be noticed later. Ban it in the linter and the model has to either work out the real type or say it cannot.
Where models actually struggle: the type level
Writing typed application code is easy for any current model. Writing types is a different skill, and it separates the field.
Conditional types, mapped types, template literal types and recursive generics are effectively a small functional language evaluated by the compiler, and models reason about it poorly. Output tends to be a plausible-looking generic that does not distribute the way it should, or that resolves to unknown in the case you cared about.
Inference is the other rough edge. Models frequently over-annotate, adding explicit type arguments where inference would have been more precise, or under-constrain a generic so it accepts more than intended.
The practical response is to treat type-level programming as the one part of TypeScript work that warrants a stronger model, and to test types the way you test code — assert that a wrong usage fails to compile, not only that a right one succeeds.
Configuration and module resolution churn
A recurring source of generated code that will not build has nothing to do with the code. Module resolution settings, extension requirements in import paths, and the difference between how a bundler and the compiler resolve a package are all configuration-dependent, and the model cannot see your configuration.
Models default to whatever was most common in training data, which skews towards older conventions. The failure looks like a broken import rather than an obvious configuration mismatch, so people blame the model.
Include the relevant compiler options in the system prompt. Four lines describing your module system, target and path aliases removes an entire recurring class of failure at negligible cost.
Types are context, and good context is cheap
The other underused property of TypeScript is that type definitions are a compact description of an interface. A model given the declaration file for a module can call it correctly without reading the implementation.
That is a very good ratio: a few hundred tokens of type declarations can replace thousands of tokens of source and produce better results, because the declarations state the contract without the noise.
For agents working in a large codebase this is a meaningful cost lever. Feed types rather than implementations wherever the task only requires calling something. Reducing token usage covers the broader pattern of trading source for summaries.
Structured output has a native fit here
If your TypeScript application needs the model to return data rather than code, the type you already have is the schema you need. Deriving a JSON schema from a TypeScript type and constraining the model with it removes parsing failures entirely, and the compiler then verifies that your consuming code matches.
This is the tightest available loop between a model's output and your application's expectations, and it is worth building even for small features. Structured outputs and JSON mode covers the constraint mechanics.
Recommendation
For application-level TypeScript with a strict compiler in the loop, DeepSeek V4 Flash or DeepSeek V4 Pro are enough, and the iteration cost is low. Let the checker grade and the cheap model retry.
For type-level work — library authoring, complex generics, public API surface — use GLM-5.2 or Kimi K3. This is the part of TypeScript where model capability genuinely shows, and where a wrong answer is expensive to unpick later.
For frontend TypeScript specifically, the type checker covers the correctness half and nothing covers the visual half, so the considerations in choosing a model for frontend code apply on top of these.
Run the type check in continuous integration on any AI-generated branch as a hard gate. It costs seconds, it is deterministic, and it catches the majority of what goes wrong before a human spends attention on it. LLMs in CI pipelines covers wiring that up without slowing the build. Compared with the untyped alternative described in the Python case, this is a structural advantage worth using.
Common questions
Does TypeScript let me use a cheaper model?
Usually yes. The compiler is a fast deterministic verifier, so a cheaper model that iterates against type errors often reaches the same clean compile for less total cost than one expensive first-shot call.
Where do models still fail at TypeScript?
Type-level programming — conditional types, mapped types, recursive generics. Models produce plausible generics that do not distribute correctly or collapse to unknown in the case you cared about. Test types by asserting that wrong usage fails to compile.
Why does generated code fail to build even when the logic is right?
Usually module resolution and compiler configuration, which the model cannot see. Including your module system, target and path aliases in the system prompt removes most of that class of failure.