Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
TypeScript Interfaces  ›  Capstone Project

Non-null Assertion and satisfies

satisfies keyword 12 minThe postfix ! asserts that a value is not null or undefined without any check, while satisfies checks a value against a type without changing the type that is inferred.

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; satisfies is 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 found

Like 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 done and it is an error),
  • there is no unknown key (a blocked key 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.
Wiring up the status coloursStep 1 of 4
  1. Declare

    Write the colours object and add satisfies Record<Status, string>.

Drills

Answer fast — trust the rule.

quick drill

What does the ! in el!.focus() do at runtime?

quick drill

getElementById("x") returns HTMLElement | null. What type does getElementById("x")! have?

quick drill

The colours object leaves out the done key and uses satisfies Record<Status, string>. What happens?

quick drill

What does satisfies give you that a type annotation does not?

Remember

  • ! is your promise; satisfies is 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.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainPromise versus proof

Compare document.getElementById("list")! with const colours = { todo: "grey", done: "green" } satisfies Record<Status, string>. Explain what each checks, what each leaves to you, and one risk of the ! operator.