Programming with AI
ENRU
9.6

AI mistakes in JS and TS, and a checklist

how to tell a fix from a gag

What it is

The chapter's closer. JavaScript and TypeScript have a peculiarity of their own: here an AI has an easy way to make the check shut up. Three characters, and the error is gone without having been fixed.

Telling a fix from a gag is the main skill in this article.

Three gags to recognize

1. any

function handle(data: any) { ... }

Switches checking off entirely. Everything coming out of an any the compiler stops looking at, and the infection spreads further through the code.

The honest replacement: unknown, which forces a check, or a proper type.

2. as

const city = data as City;

A promise to the compiler, taken apart in article 9.5. More dangerous than any, because it looks like typed code.

When it is justified: rarely and deliberately — after a check of your own that the compiler doesn't understand, for instance. Then there should be a comment next to it saying why.

3. @ts-ignore and @ts-expect-error

// @ts-ignore
widget.render(options);

Switches off checking for the next line. Always needs an explanation: if the reason isn't written down, in a month nobody will remember what is hidden here.

The other frequent mistakes

  1. strict is off. Without it there are no null checks — the main benefit (article 9.3).

  2. Promise.all instead of allSettled when a partial result is wanted (article 9.2).

  3. forEach(async ...). Doesn't wait. Always a mistake if the result is needed.

  4. == instead of ===, and || instead of ?? (article 9.1).

  5. Types duplicated by hand. A validation schema in one place, an interface in another — they will drift apart.

  6. Code for old versions. var, .then() instead of await, class components. There is more training data from that era (article 3.1, reason 3).

  7. A library method that doesn't exist. A hallucination (article 4.8). In TypeScript the compiler catches it — if the library has types.

A JS and TS checklist

Five steps on top of the general acceptance checklist (article 4.7).

1. Type checking

npx tsc --noEmit

The highest-value step. It catches five classes of error from article 9.5.

2. Search the diff for gags

git diff | grep -nE "\bany\b|\bas [A-Z]|ts-ignore|ts-expect-error"

One command shows every place where checking is switched off. Each one must be explainable.

3. The linter

npx eslint .

It catches what types can't see: ==, unused variables, forgotten awaits, forEach with async (given the right rules).

4. Run it

npx tsx app.ts

A type is no guarantee that outside data will arrive in the right shape (article 9.5). Running it is mandatory.

5. The boundary

Look at where external data enters the code: fetch, JSON.parse, localStorage, forms. Is there a run-time check everywhere, rather than just an as?

What to tell the AI

Prompt
Check your code against the list:
- are there any, as or ts-ignore, and why
- is strict on and does tsc --noEmit pass
- is there == instead of === or || where ?? is needed
- is there a forEach with async inside
- Promise.all where a partial result is wanted
- where does data come in from outside and is it validated

Which of these are there? If none, say so.

And separately, a request that saves a whole round of back-and-forth:

Prompt
Fix the type errors properly. Don't use any, as or ts-ignore.
If an error can't be fixed without them — explain why, and
don't fix it.

That last sentence matters: it gives the AI permission to say "I can't" instead of quietly gagging the check (articles 3.8, 4.8).

Cheat sheet

  • Three gags: any, as, ts-ignore. If they appeared after "fix the types", the errors are hidden, not fixed.

  • unknown is the honest replacement for any.

  • Checklist: tsc --noEmit · search the diff for gags · linter · run · the boundary.

  • git diff | grep -E "\bany\b|\bas [A-Z]|ts-ignore" — one command for the whole diff.

  • Types don't check external data: running it is mandatory.

  • Ask for fixes without gags, and allow "I can't" as an answer.