Non-null Assertion and satisfies
Two small operators finish the course, and they sit at opposite ends of trust. The ! operator says "trust me, this is not null" and checks nothing. The satisfies keyword says "check this against a type" without throwing away what the compiler already knows.
The rule
!is your promise;satisfiesis the compiler's proof.
The non-null assertion !. Put ! after an expression to remove null and undefined from its type:
const list = document.getElementById("task-list")!;
// type: HTMLElement, not HTMLElement | null
const first = tasks.find(t => t.id === 1)!;
first.title; // allowed: the compiler believes it was foundLike as, it is erased when compiled and checks nothing at runtime. If there is no element with id task-list, list is null and the next line that uses it throws. Use ! only when you are certain, and prefer a real check (if (!list) throw new Error("missing #task-list")) when the value could plausibly be absent.
The satisfies operator. Sometimes you want the compiler to verify a value against a type, but keep the more precise type it infers. An annotation cannot do both:
type Status = "todo" | "doing" | "done";
const colours = {
todo: "grey",
doing: "orange",
done: "green",
} satisfies Record<Status, string>;Record<Status, string> means "an object with a string for each status". satisfies checks that:
- every status has a colour (leave out
doneand it is an error), - there is no unknown key (a
blockedkey is flagged), - every value is a string.
Yet the type of colours stays exactly what was written, keys and all. Compare an annotation, const colours: Record<Status, string> = {...}: the check is the same, but the variable's type becomes the annotated type and any extra precision in the value is lost. With satisfies you get the check and keep the inferred type.
It works well with as const too: {...} as const satisfies Config keeps literal values while still verifying the shape.
The shortcut
Ask "Who is doing the checking?"
- Me, with no check in the code →
!. Only when absence is truly impossible. - The compiler, keeping the inferred type →
satisfies. - The compiler, and I want the declared type → a normal annotation.
Declare
Write the
coloursobject and addsatisfies Record<Status, string>.
Drills
Answer fast — trust the rule.
What does the ! in el!.focus() do at runtime?
getElementById("x") returns HTMLElement | null. What type does getElementById("x")! have?
The colours object leaves out the done key and uses satisfies Record<Status, string>. What happens?
What does satisfies give you that a type annotation does not?
Remember
!is your promise;satisfiesis the compiler's proof.- Ask who is doing the checking: you, or the compiler.
- Trap:
!on a value that can really be missing turns a compile error into a runtime crash.
