Programming with AI
ENRU
9.3

What TypeScript adds

the same JavaScript, but checked

What it is

TypeScript is JavaScript plus type descriptions. The browser doesn't execute it: before running, the types are erased, and ordinary JavaScript is left. All of the benefit happens before the run, at the moment of checking.

For working with an AI that is decisive. Go back to article 6.3: there mypy caught a forgotten null check in Python before anything ran. TypeScript does the same for the frontend — and does it more strictly, because here types are not an optional extra but the normal way of working.

What it looks like in code

interface City {
  name: string;
  temp: number | null;
  tags?: string[];
}

function report(c: City): string {
  if (c.temp === null) return `${c.name}: no data`;
  return `${c.name}: ${c.temp > 0 ? "+" : ""}${c.temp}°`;
}

const cities: City[] = [
  { name: "Oslo", temp: 4 },
  { name: "Tromsø", temp: null },
  { name: "Bergen", temp: -2, tags: ["rain"] },
];

cities.forEach(c => console.log(report(c)));
measured
tsc: no errors
Oslo: +4°
Tromsø: no data
Bergen: -2°

How to read it

interface City { ... } — a description of an object's shape. Not a class, not code: after compilation nothing is left of it.

temp: number | null — a union, "either one or the other". The most useful notation in TypeScript: it says outright that the value may be missing, and the compiler will force you to check.

tags?: string[] — an optional field. Its type is in fact string[] | undefined.

function report(c: City): string — takes a City, returns a string. The same thing as a signature in Python (article 6.5), and it works the same way: it replaces a paragraph of explanation.

What happens without the null check

Remove the if and ask the compiler to check:

function report(c: City): string {
  return `${c.name}: ${c.temp.toFixed(1)}°`;
}
measured
error TS18047: 'c.temp' is possibly 'null'.

The code won't compile. It won't fail for a user, won't slip past the tests — it simply won't build until you add the check.

The same error in Python looked like Item "None" of "str | None" has no attribute "upper", and in JavaScript without types like Cannot read properties of null already at run time (article 8.4). Three languages, one class of error, and only here is it caught before the run.

strict isn't an option, it is the point

{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true
  }
}

Without strict the null check is off, and TypeScript turns into decoration. Switching it on is mandatory.

noUncheckedIndexedAccess is the second most useful setting. With it, indexing gives T | undefined, because the element may not be there:

const cities = ["Oslo", "Bergen"];
const first: string = cities[5];
measured
error TS2322: Type 'string | undefined' is not assignable to type 'string'.

The compiler reminds you of the thing everyone forgets: an index can go out of bounds.

How to run it

npx tsc --noEmit        # only check, don't build anything
npx tsx app.ts          # run directly

--noEmit is what you want most often: a check without a build, the counterpart of mypy --strict.

What to tell the AI

Prompt
Port this module to TypeScript: interfaces for the data, types
on every function. tsc --noEmit with strict must pass.
Don't change the logic.
Prompt
In tsconfig turn on strict and noUncheckedIndexedAccess.
Fix the errors that appear without disabling the checks and
without as any.

That second sentence matters: the simplest way to "fix" a type error is to silence it, and an AI is inclined to do exactly that (article 9.6).

Prompt
Describe the types for this API's response: here is a sample
JSON. Mark fields that may be absent as optional or | null —
honestly, not "so that it compiles".

What to look for

strict: false, or its absence. The null check is off and the main benefit is lost.

any in the types. Switches checking off completely for that spot.

as to calm the compiler down. That gets its own treatment in article 9.5.

Types written but tsc never run. The same as annotations without mypy: decoration that has drifted away from the code.

.js files mixed in with .ts. A half-finished migration: there are no checks in the .js files, and the errors hide there.

Cheat sheet

  • TypeScript is JavaScript plus types; at run time the types are erased.

  • interface describes a shape, | is a union, ? an optional field.

  • number | null forces an emptiness check — otherwise the code won't build.

  • strict: true is mandatory, or there is no null checking.

  • noUncheckedIndexedAccess — indexing gives | undefined.

  • npx tsc --noEmit is a check without a build, the counterpart of mypy --strict.