genericsdarttypesdynamic-typing

How can I make a more generic isEmpty() function in Dart?


I have the following utility function for checking if a String variable is empty or null:

bool isEmpty(String s){
  return (s??'').isEmpty;
}

Now I want to make a similar function for Iterables. The implementation is trivial:

bool isEmpty(Iterable i){
  return (i??[]).isEmpty;
}

But now I have to either name the two functions differently, or somehow merge them into one. Here's where I run into trouble.

I can make the variable dynamic:

bool isEmpty(dynamic x){
  if( x is String) return (x??'').isEmpty;
  if( x is Iterable) return (x??[]).isEmpty;
  throw UnimplementedError('isEmpty() is not defined for the type ${x.runtimeType}');
}

but then x will be of type Null if I pass String s = null or Set s = null. What if in the future I want to treat null differently for Iterable and String?

I could make the function generic:

bool isEmpty<T>(T x){
  if( T == String) return ((x as String)??'').isEmpty;
  if( T == Iterable) return ((x as Iterable)??[]).isEmpty;
  throw UnimplementedError('isEmpty() is not defined for the type $T');
}

but now it will throw an exception if I pass a List, Set or anything else that is a subtype of Iterable, but not an actual Iterable.

How can I make one single isEmpty() function that works exactly as the two separate ones that accept String and Iterable?


Solution

  • You can make extensions. Like these:

    extension StringExt on String {
      bool isNullOrEmpty() => this == null || this.isEmpty;
    }
    
    extension IterableExt<T> on Iterable<T> {
      bool isNullOrEmpty() => this == null || this.isEmpty;
    }
    

    I've renamed isEmpty to isNullOrEmpty due to name conflicts (String.isEmpty, Iterable<T>.isEmpty...).