States with their own data
A food delivery order is placed, then out for delivery (with a rider), then delivered (with a time) — or cancelled (with a reason). Model it with a discriminated union so impossible combinations can't exist.
Model the states first
Real delivery and booking apps model their orders exactly like this, often with many more states.
Before any code, list every state an order can be in and the data that exists only in that state. A placed order has just an id. A delivering order also has a rider. A delivered order has a delivery time. A cancelled order has a reason. Writing these as separate union members means an order can never be "delivered without a time" or "cancelled with a rider" — those combinations simply can't be built.
type Order =
| { status: "placed"; id: number }
| { status: "delivering"; id: number; rider: string }
| { status: "delivered"; id: number; at: string }
| { status: "cancelled"; id: number; reason: string };This idea — "make illegal states unrepresentable" — is one of the most valuable habits typed programming teaches. Bugs that would need tests and careful checks in plain JavaScript are ruled out by the shape of the data itself.