Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A type parameter bound restricts the set of concrete types that can be substituted for a generic type parameter. By applying a bound, the Dart type system guarantees that any type argument provided at compile time is a subtype of the specified bounding type. This allows the compiler to safely resolve method and property lookups on instances of the generic type against the interface of the bound. In Dart, type bounds are declared using the extends keyword within the angle brackets < > of a generic class, mixin, interface, or method declaration.
class GenericDeclaration<T extends BoundingType> {
  // Implementation
}

Subtyping Rules and Compiler Behavior

When a bound is established as <T extends BoundingType>, any type argument X supplied during instantiation must satisfy the subtyping relationship X <: BoundingType. If X is not a subtype of BoundingType, the Dart analyzer emits a compile-time error. Because the compiler enforces this constraint, instances of T within the generic scope are treated as instances of BoundingType.
class Base {
  void baseMethod() {}
}

class Derived extends Base {}

// T is constrained to Base or its subtypes
class Container<T extends Base> {
  final T instance;
  
  Container(this.instance);

  void invoke() {
    // The compiler allows this because T is guaranteed to extend Base
    instance.baseMethod(); 
  }
}

// Valid: Derived <: Base
Container<Derived> validContainer = Container(Derived());

// Invalid: String is not a subtype of Base
// Container<String> invalidContainer = Container("text"); // Compile-time error

Implicit and Nullability Bounds

If a generic type parameter is declared without an explicit bound (e.g., <T>), Dart implicitly applies the top type Object? as the bound. This means the type parameter accepts any type, including nullable types. To explicitly restrict a type parameter to non-nullable types, the bound must be set to the non-nullable Object.
// Implicitly <T extends Object?>
class Unbounded<T> {
  T? value; // T can be nullable (e.g., String?)
}

// Explicitly restricted to non-nullable types
class NonNullableBound<T extends Object> {
  T value; // T cannot be a nullable type (e.g., String? is invalid)
  
  NonNullableBound(this.value);
}

F-Bounded Quantification (Recursive Bounds)

Dart supports F-bounded polymorphism, allowing a type parameter to appear within its own bound. This is strictly used when the bounding type is itself a generic type that requires the implementing type as an argument.
abstract class Equatable<T> {
  bool isEquivalent(T other);
}

// T is constrained by a generic interface parameterized over T itself
class Processor<T extends Equatable<T>> {
  bool compare(T a, T b) {
    return a.isEquivalent(b);
  }
}

Multiple Bounds

Dart does not support intersection types for generic bounds (e.g., <T extends TypeA & TypeB>). A type parameter can only have a single explicit extends clause. To enforce multiple constraints, the bounding type itself must be a composite interface (an abstract class or mixin) that implements the required supertypes.
Master Dart with Deep Grasping Methodology!Learn More