DART / CHAPTER 05

Generic contracts and runtime element checks

Intermediate

Preserve type relationships while respecting collection variance.

By the end of this chapter

  • Write a type-preserving helper
  • Bound a generic parameter by required behavior
  • Distinguish static views from runtime list element types

The idea

A generic function connects related input and output types. A helper taking List<T> can return a T without discarding whether its elements are strings, numbers, or domain objects. Bounds express capabilities required by the implementation. Avoid replacing a meaningful type relationship with dynamic simply because several concrete types should be supported.

Dart retains generic type information at runtime. Its covariant collection relationships can allow a narrower list to be viewed through a broader element type, but writes still face runtime checks against the actual list type. A List<int> does not become a list capable of storing every num merely because a reference is typed List<num>. Prefer read-only behavior or a fresh appropriately typed collection when widening a mutable API.

Dart example
T first<T>(List<T> values) {
  if (values.isEmpty) throw StateError("empty");
  return values.first;
}
void main() { print(first<String>(["a","b"])); }
Result
a

The type parameter connects the element type to the result type. Empty input is explicitly outside this helper’s successful contract.

Try it yourself

Write a generic helper returning the last value or a supplied fallback for an empty list.

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

CHECK YOUR UNDERSTANDING

Chapter checkpoint

What does a type parameter primarily preserve in a first-element helper?