Type safety turns assumptions into contracts. Compilers, builders, validators,
and CI turn those contracts into feedback loops. Four of them:
01 the origin — API drift
I learned this wiring a TypeScript frontend to a separate backend through an OpenAPI contract.
(the full CoralEHR origin story lands here)
type User = { id: string; displayName: string };
user.name
TypeScript turns a rename into a compiler-guided checklist.
02 what TypeScript achieves — impossible states
loose
type State = {
loading: boolean;
error?: string;
user?: User;
};
typed
type State =
| { status: "loading" }
| { status: "error"; message: string }
| { status: "success"; user: User };
Good types make invalid states unrepresentable — a class of tests you never write.
03 how a compiler thinks — type-loupe
Edit the code — a hand-built checker runs live in your browser. No TypeScript shipped; type-loupe is its own tiny parser + checker.
04 the agent loop — the model guesses, the compiler checks
Watch it run: the agent writes FHIR, the compiler rejects it, the agent reasons and patches with fhir-craft, the check passes — on a loop. (fixture replay — real tool output, no live model)
patient.ts
import { createPatient, humanName } from "fhir-craft";
const patient = createPatient({
given: "John", family: "Smith",
});
Animation: an agent writes a FHIR Patient with name as a string; the compiler reports it is not assignable to HumanName[]; the agent reasons and rewrites it with fhir-craft's createPatient; the type check passes. Loops continuously.