Notes on typing JSON.

Short essays on the decisions behind the inferred types this site produces. Opinionated, specific, and occasionally wrong.

  1. 01

    interface vs type: when each one earns it

    interface and type look identical in most code. The other 10% of the language is where every codebase that mixed them at random eventually wishes it had picked a rule sooner. The boring answer is the one that scales.

    · 6 min read

  2. 02

    JSON has no dates

    Every date in your payload is encoded as a string or a number, by convention. The convention is a contract you usually inherited rather than chose, and the choice changes how every consumer parses, sorts, and displays the value.

    · 7 min read

  3. 03

    TypedDict vs dataclass vs Pydantic: a three-way trade-off

    TypedDict is a type-checker fiction over a real dict. Dataclass is a real class with no validation. Pydantic is a real class that validates. They are not interchangeable, and most codebases that pick one for everything are paying for it somewhere.

    · 7 min read

  4. 04

    Optional[T] or T | None: pick one

    A field that is always sent but sometimes null is not the same as a field that is sometimes missing. Python lets you collapse both into Optional[T] = None and pretend they are the same. They are not.

    · 6 min read

  5. 05

    Pydantic vs dataclasses: when the runtime cost earns its keep

    A dataclass is free at runtime. A Pydantic model is not — it actually validates. The question is where the validation cost buys you something, and where it is insurance on a risk that does not exist.

    · 6 min read

  6. 06

    Nullable or optional: pick one

    A nullable field and a sometimes-absent field are different promises. Collapsing them into one optional type loses the signal the server was trying to send.

    · 5 min read

  7. 07

    When "string" is a lie

    A field that is always "customer" is not a string. It is a discriminant. The tool gives you string anyway, on purpose. Here is why every alternative is worse, and where the lie actually lives.

    · 6 min read

  8. 08

    Zod vs types: when the runtime cost earns its keep

    Zod ships 14 KB of JavaScript and spends CPU on every parse. The question is not whether that is acceptable, but where the spend actually buys you something.

    · 6 min read

  9. 09

    Go struct tags: explicit beats clever

    The explicit per-field json tag looks verbose until you need to audit, grep, or change it. Then it becomes the reason the struct is still readable.

    · 6 min read

  10. 10

    Why we do not re-infer from JSON Schema

    A schema is what they promised. A sample is what they sent. Both are useful. Conflating them hides the places they disagree, which are the places that matter.

    · 5 min read