DART / CHAPTER 10

Sealed results and exhaustive branching

Advanced

Model alternatives so every result state is handled.

By the end of this chapter

  • Define a closed family of result variants
  • Destructure variants in switch patterns
  • Make new states visible to exhaustive checks

The idea

A sealed class restricts direct subtypes to its library and lets the compiler reason about a known family of alternatives. Represent successful and failed outcomes as separate variants rather than one class with unrelated nullable fields. Each variant can then require exactly the data that makes sense in that state, reducing impossible combinations.

A switch expression can pattern-match the variant and extract its fields. Exhaustiveness checking highlights missing cases when the sealed family changes. Avoid an unnecessary catch-all when the goal is for new variants to trigger review. Static exhaustiveness does not validate arbitrary external messages; decode and validate those at the boundary before constructing trusted result objects.

Dart example
sealed class Outcome {}
class Good extends Outcome { final int value; Good(this.value); }
class Bad extends Outcome { final String reason; Bad(this.reason); }
String describe(Outcome o) => switch(o) { Good(:final value) => "value=$value", Bad(:final reason) => reason };
void main() { print(describe(Good(4))); }
Result
value=4

The Good pattern extracts its required value. The two cases cover the known Outcome variants.

Try it yourself

Model pending, success, and failure variants with an exhaustive display function.

Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.

CHECK YOUR UNDERSTANDING

Chapter checkpoint

Why use separate success and failure variants instead of two unrelated nullable fields?