AI Generated TypeScript Errors

AI produces code that doesn't type-check — usually wrong types, any-casts, or missing generic parameters.

Cursor / Claude Code / Copilot writes a chunk of TypeScript, you commit, then tsc --noEmit spits out a wall of TS2322: Type 'string' is not assignable to type 'number' or TS2339: Property 'foo' does not exist on type 'unknown'. LLMs have no runtime type reflection — they guess the shape of an interface from training data, which is fragile around generics, union types, and third-party .d.ts files. This page breaks down the 5 high-frequency root causes and gives an agent-loop pattern that lets the AI fix its own type errors.

Common causes

Ordered by hit rate, highest first.

1. AI guessed at a signature it hadn’t read

The most common failure: when calling a third-party library, the AI writes code matching a remembered older version, but you installed the new one and the API has changed.

// AI wrote (old Stripe SDK)
const charge = await stripe.charges.create({ amount: 1000, currency: "usd" });
// New SDK uses PaymentIntents
// → TS2554: Expected 0 arguments, but got 1

How to spot it: error codes are TS2554 (wrong arg count) / TS2345 (wrong arg type) / TS2339 (property missing), and the offending line is a third-party call.

2. Cast to any / as unknown as T hides the real error

When the AI can’t make types fit, it slaps as any or as unknown as Foo on. The compiler is happy; the runtime crashes.

const data = (response as any).user.email;   // response is actually { error: string }
// Compiles fine, runtime: TypeError: Cannot read property 'email' of undefined

How to spot it: grep -n "as any\|as unknown as" src/ and count. More than 2 in an AI-authored block is almost always avoidance.

3. Missing or wrong generic parameters

Array.prototype.reduce, useState, useRef, Map / Set, custom generic functions — AI often assumes TypeScript will infer correctly, but inference lands on never[] or unknown.

// AI wrote
const items = [].reduce((acc, x) => {
  acc.push(x.name);   // TS2339: Property 'push' does not exist on type 'never'
  return acc;
}, []);

// Correct
const items = [] as string[];
// or
const items = [].reduce<string[]>((acc, x) => { acc.push(x.name); return acc; }, []);

How to spot it: errors mention never / unknown / Argument of type 'X' is not assignable to parameter of type 'never'.

4. Strict mode null / undefined not handled

A lot of training data is from pre-strict-null code, so the AI assumes fields always exist. Turn on strictNullChecks and TS2532: Object is possibly 'undefined' is everywhere.

function getEmail(user: User | null) {
  return user.email;   // TS18047: 'user' is possibly 'null'
}

How to spot it: error codes TS2531 / TS2532 / TS18047 / TS18048 with "strict": true in tsconfig.json.

5. Mixing type and value imports / missing import type

With verbatimModuleSyntax or isolatedModules on, type-only imports must use import type. AI frequently forgets.

import { User } from "./types";   // If User is type-only: TS1484 or runtime "undefined"
// Correct
import type { User } from "./types";

How to spot it: TS1484: 'User' is a type and must be imported using a type-only import or runtime “User is undefined.”

Shortest path to fix

Ordered by ROI. Steps 1+2+3 are the standard fix loop for AI-generated type errors.

Step 1: Run tsc --noEmit to surface every error at once

Don’t re-prompt the AI on the first error you see. Get the whole list first so the AI sees the full picture:

npx tsc --noEmit --pretty false > ts-errors.txt
wc -l ts-errors.txt

--pretty false disables color (cleaner to paste back); --incremental false forces a fresh compile if you suspect .tsbuildinfo is lying.

Step 2: Paste the verbatim errors (with file:line) back to the AI

Don’t paraphrase or summarize. The more complete the original, the more accurate the fix:

I ran tsc --noEmit and got the errors below. Please:
1. Do NOT use `as any` or `as unknown as`
2. Do NOT add // @ts-ignore / // @ts-expect-error to silence anything
3. If a third-party lib's types are broken, tell me which version to upgrade
   or which @types/* to install
4. Reply with a diff and explain each change

Error log:
src/api/user.ts:34:7 - error TS2322: Type 'string' is not assignable to type 'number'.
src/api/user.ts:41:12 - error TS2339: Property 'email' does not exist on type 'unknown'.
[paste in full]

Step 3: Put the AI in a self-verifying loop

Claude Code / Cursor Composer / Aider can run shell commands. Configure them so the AI must re-run tsc after each change:

For this task you must:
1. Fix the type errors
2. After each change, run `npx tsc --noEmit`
3. If errors remain, keep going. Max 5 iterations.
4. After 5, stop and tell me which errors you couldn't fix; paste them verbatim.

In Aider: --auto-test --test-cmd "npx tsc --noEmit".

Step 4: Wrong third-party types → find @types/* or upstream .d.ts

Many npm packages get community-maintained types:

npm install -D @types/node @types/react @types/lodash
# Does the library ship its own types?
npm view <pkg> types
# No @types and no bundled types → have the AI write a minimal declaration
echo 'declare module "untyped-lib";' > src/types/untyped-lib.d.ts

This beats as any because a module declaration can be filled in incrementally.

Step 5: Common TS error code → fix direction

CodeMeaningFix direction
TS2322Type mismatchCheck both sides of the assignment; add an explicit type annotation
TS2339Property missingType is too wide; add a type guard / in check / assertion
TS2345Wrong arg typeInspect the signature; fix the caller’s value
TS2554Wrong arg countLibrary version changed; check the changelog
TS2531/2532null/undefined unhandledAdd if (x) guard / x?.foo / x!
TS7006Implicit anyAdd an explicit parameter type
TS2304Cannot find nameMissing import / missing @types/*
TS1484Type-only import requiredChange to import type
TS2741Missing required propertyInspect the object literal; add the field

Prevention

  • Put tsc --noEmit in the AI’s agent loop and in pre-commit hooks; failing types block the commit
  • In CLAUDE.md / .cursorrules: ban as any, ban @ts-ignore, ban @ts-expect-error without a reason comment
  • Set "strict": true and "noUncheckedIndexedAccess": true in tsconfig.json to force null handling
  • In code review, grep for as any / // @ts- with zero tolerance
  • When upgrading third-party deps, have the AI also re-check whether @types/* needs a matching bump
  • Run tsc --noEmit --extendedDiagnostics occasionally to catch AI-generated recursive unions that tank compile speed

Tags: #AI coding #Debug #Troubleshooting