The error you already know

You parse a webhook body and read a field off it, the way you have a thousand times. In Python this is json.loads, and a typo raises a KeyError or an AttributeError the moment the line runs. Hard to miss.

handler.ts
// A webhook body arrives as
// text, and you parse it.
const data: any = JSON.parse(body);
const name = data.usrnameNo squiggle here. any switched the compiler off, so the typo compiles and reads undefined at runtime.;

You wrote any on the parsed value, and the compiler went quiet right where Python would have shouted. The typo compiles, name is undefined, and the bad value travels on.

All any did was make the compiler stop looking. That is a worse deal than untyped Python, which at least fails at the moment of the mistake. any fails later, somewhere else, with no trail back to this line.

tsc stays silent. At runtime name is undefined, and the bad value travels on to whoever called you.

Watch the habit become the idiom

The honest move is the one your Python already makes at every I/O edge. You do not trust the parsed body, so you check its shape before you read it. TypeScript has a type for exactly that posture of not knowing yet: unknown.

unknown accepts every value, the same as any. The difference is what it lets you do next. With any you can read any field right away. With unknown you can read nothing until you have proven what you are holding. The compiler keeps the value behind a gate, and the isinstance check is the key you already carry.

Step through it at your own pace. Each click is one move.

Step 1 of 4

Python guards untrusted input before it reads a field. Your isinstance reflex.

handler.pyPython
data = json.loads(body)

# guard untrusted input before reading
if isinstance(data, dict):
    return data.get("name", "friend")

Here's what each move does.

  • Start where Python does, guarding the parsed body with a shape check before you read it.
  • Ported on autopilot, the field read happens through any, and a typo sails straight through.
  • Swap any for unknown and the same read stops compiling, because you have not proven the shape yet.
  • Narrow once with the checks you already know, and inside that branch the field is real and typed.

any is a one-way valve. Open it and every value downstream loses its types too, quietly, for as long as that value lives. unknown is the opposite posture. It is the default for anything you did not write yourself, parsed JSON, an API response, a message off a queue. You keep the valve shut and narrow at the edge, which is the discipline you brought with you.

Now make the compiler agree

readName takes a parsed body and pulls a name out of it. Right now the parameter is typed too loosely, so the compiler trusts a field that might not exist. Tighten the parameter to the honest type, then narrow before you read, and return a real string for every input. No any.

Loading...
type-checking…