letOrNull<T> function

T? letOrNull<T>(
  1. dynamic input
)

Attempts to convert a dynamic input to the specified type T, returning Null on failure.

This is a high-level dispatcher that uses more specific let...OrNull helpers based on the target type T.

Supported types (each also in its nullable form, e.g. int?):

If input already is a T, it is returned unchanged, whatever T is. A null input returns null.

Throws ArgumentError when input would need converting into a type this dispatcher cannot produce, rather than returning a null that looks like an ordinary conversion failure:

  • Specific collection types such as List<int> or Map<String, int>. Use letListOrNull, letSetOrNull, letIterableOrNull or letMapOrNull with the element types instead.
  • Enums. An enum value cannot be found from the type alone. Look it up by name with MyEnum.values.valueOf(...) (returns null on a miss) or MyEnum.values.valueOfOrThrow(...).

Implementation

T? letOrNull<T>(dynamic input) {
  if (input is T) return input;
  if (input == null) return null;
  // Enforced in release as well: a stripped assert would let a misuse like
  // `letOrNull<List<int>>('[1]')` silently return null, which is
  // indistinguishable from a real conversion failure. For medical-grade
  // code that's an unacceptable silent failure.
  if (_isSpecificCollectionType<T>()) {
    throw ArgumentError(
      'letOrNull<$T> cannot convert a ${input.runtimeType} into a specific '
      'collection type. Only List<dynamic>, Set<dynamic>, Iterable<dynamic> '
      'and Map<dynamic, dynamic> (or their nullable forms) are converted; use '
      'letListOrNull, letSetOrNull, letIterableOrNull or letMapOrNull with '
      'the element types instead.',
    );
  }
  if (_isEnumType<T>()) {
    throw ArgumentError(
      'letOrNull<$T> cannot convert a ${input.runtimeType} into an enum. '
      'Look the value up by name with $T.values.valueOf(...) or '
      '$T.values.valueOfOrThrow(...) instead.',
    );
  }
  final raw = () {
    if (typeEquality<T, num>() || typeEquality<T, num?>()) {
      return letNumOrNull(input);
    } else if (typeEquality<T, double>() || typeEquality<T, double?>()) {
      return letDoubleOrNull(input);
    } else if (typeEquality<T, int>() || typeEquality<T, int?>()) {
      return letIntOrNull(input);
    } else if (typeEquality<T, bool>() || typeEquality<T, bool?>()) {
      return letBoolOrNull(input);
    } else if (typeEquality<T, DateTime>() || typeEquality<T, DateTime?>()) {
      return letDateTimeOrNull(input);
    } else if (typeEquality<T, Uri>() || typeEquality<T, Uri?>()) {
      return letUriOrNull(input);
    } else if (isSubtype<T, List<dynamic>?>()) {
      return letListOrNull<dynamic>(input);
    } else if (isSubtype<T, Set<dynamic>?>()) {
      return letSetOrNull<dynamic>(input);
    } else if (isSubtype<T, Iterable<dynamic>?>()) {
      return letIterableOrNull<dynamic>(input);
    } else if (isSubtype<T, Map<dynamic, dynamic>?>()) {
      return letMapOrNull<dynamic, dynamic>(input);
    } else if (typeEquality<T, String>() || typeEquality<T, String?>()) {
      return letAsStringOrNull(input);
    }
    return input;
  }();

  return letAsOrNull<T>(raw);
}