'In Dart, syntactically nice way to cast dynamic to given type or return null?

I have a dynamic x and I would like to assign x to T s if x is T, and otherwise assign null to s. Specifically, I would like to avoid having to type x twice, and to avoid creating a temporary. (For example, I don't want to have to write String s = map['key'] is String ? map['key'] : null; over and over, because I will have many such expressions.) I don't want there to be any possibility of a runtime error.

The following works:

class Cast<T> {
  T f(x) {
    if (x is T) {
      return x;
    } else {
      return null;
    }
  }
}

// ...

dynamic x = something();
String s = Cast<String>().f(x);

Is there a syntactically nicer way to do this?



Solution 1:[1]

I use the following utility function, which allows for an optional fallback value and error logging.

T tryCast<T>(dynamic x, {T fallback}){
    try{
        return (x as T);
    }
    on CastError catch(e){
        print('CastError when trying to cast $x to $T!');
        return fallback;
    }
}
var x = something();
String s = tryCast(x, fallback: 'nothing');

Solution 2:[2]

Just use the as keyword

final tweet = tweets[index] as Tweet;

Solution 3:[3]

I'm using those with Dart null safety (Dart SDK >= 2.12):

T? castOrNull<T>(dynamic x) => x is T ? x : null;

T castOrFallback<T>(dynamic x, T fallback) => x is T ? x : fallback;

Solution 4:[4]

A combination of both prior two posts, without the logging.

Fallback defaults to null when not provided.

T cast<T>(dynamic x, {T fallback}) => x is T ? x : fallback;

Solution 5:[5]

CastError is deprecated, Instead use TypeError.

With null safety, you can try the below snippet. Where fallback is optional/nullable.

T? tryCast<T>(dynamic value, {T? fallback}) {
  try {
    return (value as T);
  } on TypeError catch (_) {
    return fallback;
  }
}

Or without fallback -

T? tryCast<T>(dynamic value) {
  try {
    return (value as T);
  } on TypeError catch (_) {
    return null;
  }
}

Usage -

final val = tryCast<String>(1) ?? "";

Solution 6:[6]

This hidden gem was provided by one of Dart-Lang's maintainers:

extension AsExtension on Object? {
  X as<X>() => this as X;
  X? asOrNull<X>() {
    var self = this;
    return self is X ? self : null;
  }
}

extension AsSubtypeExtension<X> on X {
  Y asSubtype<Y extends X>() => this as Y;
}

extension AsNotNullExtension<X> on X? {
  X asNotNull() => this as X;
}

// example
void main() {
  num? n = 1 as dynamic;
  n.as<int>().isEven;
  n.asSubtype<int>().isEven; // `n.asSubtype<String>()` is an error.
  n.asNotNull().floor();
  n.asOrNull<int>()?.isEven; // Corresponds to `(n as? int)?.isEven`.
}

NOTE: If your object is of type dynamic, you have to cast it Object? first. The explanation for this can be found here: first one by Erik, a dart maintainer @Google and the second by a community member. Basically it boils down to dart not calling extension methods on receives of one of the following three types: dynamic, Never, or void as stated here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 dave o grady
Solution 3 MatPag
Solution 4 Ryan Huebert
Solution 5
Solution 6