TypeScript course · Part 5: Project · lab 5 of 5
Project: type the quote module end to end
Take the quote feature you built in JavaScript and give it types that are worth having: a model nothing can contradict, a checked boundary, states that cannot be impossible, and errors you cannot forget to handle.
By the end you can
- Model a small domain so wrong values cannot be written down
- Check untrusted input once, at the edge, and return a typed result
- Model a feature's states as a union rather than loose flags
- Return failures as values the compiler makes you handle
Easier if you did first
step 1 of 4
The model
Everything starts with the model. Get it right and most of the bugs you would have written simply cannot be expressed.
Three decisions do the work here.
The menu is a literal union, not a string, so a typo is a compile error and your editor offers the real options.
The rate table is a Record keyed by that union, written with satisfies, so every menu must have a price, no price can be a string, and no fourth menu can sneak in. Leave one out and the compiler tells you.
The calculation is a plain function from values to a number: no objects reaching into global state, nothing to mock when you test it.
One more piece worth having early: a Draft, built with Partial, for an order that is still being filled in. Your form holds a Draft; only a complete, checked Order ever reaches the rest of the program.
Build the model.
type Menu = "buffet" | "plated" | "finger";
type Order = { guests: number; menu: Menu; notes?: string };
type Draft = Partial<Order>;
const RATES = { buffet: 45, plated: 80, finger: 25 } satisfies Record<Menu, number>;
function totalFor(order: Order): number {
return order.guests * RATES[order.menu];
}What these words mean
- literal union
- A type that is one of a few exact values.
- Record<Menu, number>
- An object with exactly those keys and number values.
- satisfies
- Check the object against that shape while keeping its exact keys.
- Partial<Order>
- The same shape with every field optional. A half-filled form.
Easy to get wrong
menu: string and a lookup that may return undefined
A literal union and a Record keyed by it
With string the compiler cannot know which menus exist, so every lookup is possibly undefined and you add checks for cases that should be impossible.
Using the same type for the form and for a finished order
Draft for the form, Order for the rest
A form is half-empty by nature. Sharing one type means either the form fights the compiler, or the rest of the program treats every field as optional forever.
What we check
- The compiler reports no errors
- Menu is a literal union
- Draft is built from Order with Partial
- RATES is checked against Menu and keeps its keys
- notes is optional
- A buffet for 20 is 900
- The other two menus price correctly
Nothing printed yet. Run your code.