isSubtypeOf method

  1. @override
bool isSubtypeOf(
  1. RuntimeType other, {
  2. Object? value,
})
override

Checks if this type is a subtype of other.

Implementation

@override
bool isSubtypeOf(RuntimeType other, {Object? value}) {
  if (other is FunctionRuntimeType) {
    // An untyped function type matches any function and vice-versa.
    if (_isUntyped || other._isUntyped) return true;
    // Return type is covariant.
    if (!_runtimeTypeCompatible(returnType, other.returnType)) return false;
    // Required positional parameters must line up 1:1 (contravariant).
    if (positionalParameterTypes.length !=
        other.positionalParameterTypes.length) {
      return false;
    }
    for (var i = 0; i < positionalParameterTypes.length; i++) {
      if (!_runtimeTypeCompatible(
        other.positionalParameterTypes[i],
        positionalParameterTypes[i],
      )) {
        return false;
      }
    }
    // Named parameters: every named parameter the supertype declares must be
    // present here with a contravariant-compatible type.
    for (final entry in other.namedParameterTypes.entries) {
      final mine = namedParameterTypes[entry.key];
      if (mine == null) return false;
      if (!_runtimeTypeCompatible(entry.value, mine)) return false;
    }
    return true;
  }
  // GEN-125 / SCD136 — a function TYPEDEF is bridged as
  // `BridgedClass(nativeType: Function, name: typedef.name)`, so its name is
  // `VoidCallback`, never `Function`. The name is the one property of that
  // bridge deliberately not `Function`, so the nominal test below could
  // never match one and every Flutter callback was refused:
  //
  //     type 'dynamic Function()' is not a subtype of type 'VoidCallback?'
  //
  // Ask what the bridge IS rather than what it is called. This subsumes the
  // nominal test — `dart:core`'s own `Function` bridge carries
  // `nativeType: Function` too — and because the argument check, the return
  // check and `is`/`as` all route through here, one rule repairs all three.
  //
  // WHY THIS FILE MAY KNOW ABOUT `BridgedClass`, given that it is the
  // interface file everything else depends on: it already did. The line
  // below is a STRING GUESS about how bridges are named, which is the same
  // coupling in its worst form. Replacing a guess about an implementor with
  // a typed question to it removes knowledge from this file rather than
  // adding it.
  //
  // Deliberately arity-blind, exactly as the nominal test it subsumes was: a
  // bridged typedef carries `nativeType: Function` and nothing else, so
  // there is no signature here to check a closure against. Making the bridge
  // carry one is a generator change, tracked as scd137. Until then the
  // posture is the documented one — be permissive rather than reject working
  // callbacks.
  if (other is BridgedClass && other.nativeType == Function) {
    // SCD137 — when the bridge carries the typedef's positional arity, a
    // callable that PROVABLY cannot be invoked is refused here instead of
    // throwing later at the call, with a message that names neither the
    // parameter nor the typedef.
    //
    // The rule is deliberately the conservative one. Only the typedef's
    // REQUIRED count is checked: that is the one invocation shape it
    // guarantees, and rejecting on a possibility is how a working callback
    // gets refused. Optional positionals on the callable side count toward
    // what it can accept, so `(a, [b]) {}` serves a one-argument typedef.
    //
    // No arity means no opinion — scd136's acceptance, unchanged. Every
    // `.b.dart` generated before the generator carried signatures through is
    // in that state, so this is inert until both have shipped.
    final required = other.typedefRequiredPositional;
    if (required == null) return true;
    final minAccepted = positionalParameterTypes.length;
    final maxAccepted = minAccepted + optionalPositionalParameterTypes.length;
    return minAccepted <= required && required <= maxAccepted;
  }
  // Every function is a `Function` and an `Object`.
  return _isWildcardTypeName(other.name) || other.name == 'Function';
}