Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Dart2, what is the correct "anything" type to use for generics?

Tags:

dart

AngularDart has a class called AppView, i.e. abstract class AppView<T> {}.

One (at least) of these are generated for every class annotated with @Component:

// file.dart
@Component(...)
class DashboardComponent {}


// file.template.dart (Generated)
class ViewDashboardComponent extends AppView<DashboardComponent> {}

I have code elsewhere in the framework that doesn't care what this T type is. I'm a little confused with Dart 2 what the "right" "anything" type to use. For example, I could use:

  • AppView
  • AppView<dynamic>
  • AppView<Object>
  • AppView<Null>
  • AppView<void>

I think more than one of these will "work". But which is the "right" one to use in this case?

like image 948
matanlurey Avatar asked Dec 13 '25 18:12

matanlurey


1 Answers

You should be fine to use AppView (or AppView<dynamic>) just about anywhere. I can think of two examples where this will get you into trouble though:

  1. If you are instantiating an AppView, you definitely want that type parameter. See the following error when you don't:

    $ cat a.dart
    void main() {                                                                                                                          
      List<dynamic> a = ["one", "two", "three"];
      List<String> b = a;
    }
    $ dart --preview-dart-2 a.dart
    Unhandled exception:
    type 'List' is not a subtype of type 'List<String>' where
      List is from dart:core
      List is from dart:core
      String is from dart:core
    
    #0      main (file:///Users/sam/a.dart:3:20)
    #1      _startIsolate.<anonymous closure> (dart:isolate/isolate_patch.dart:279:19)
    #2      _RawReceivePortImpl._handleMessage (dart:isolate/isolate_patch.dart:165:12)
    
  2. If you are ever assigning a closure to a site that expects a closure with one or more typed parameters that involve T, you will see a "uses dynamic as bottom" static error (from the analyzer), and probably a runtime error as well:

    $ cat f.dart                                                       
    
    void main() {
      List a = <String>["one", "two", "three"];
      a.map((String s) => s.toUpperCase());
    
      List b = ["one", "two", "three"];
      b.map((String s) => s.toUpperCase());
    }
    $ dart --preview-dart-2 f.dart
    f.dart:3:9: Error: A value of type '(dart.core::String) → dart.core::String' can't be assigned to a variable of type '(dynamic) → dynamic'.
    Try changing the type of the left hand side, or casting the right hand side to '(dynamic) → dynamic'.
      a.map((String s) => s.toUpperCase());
            ^
    f.dart:6:9: Error: A value of type '(dart.core::String) → dart.core::String' can't be assigned to a variable of type '(dynamic) → dynamic'.
    Try changing the type of the left hand side, or casting the right hand side to '(dynamic) → dynamic'.
      b.map((String s) => s.toUpperCase());
            ^
    

(I'm not certain any Dart tool yet has complete Dart 2 runtime and compile time semantics, so this might change slightly.)

In these cases, it is best to use generic classes, generic methods, and generic typedefs to encapsulate, for a given scope, what the values of an object's type parameters might be.

I suspect there is a difference between dynamic and Object in Dart 2, and I think Günter covered this in his response, though if your code "doesn't care what this T type is", then you're probably not calling any methods on the component.

Edit: void

AppView<void> might be a good choice in this case, as an actual check that you actually never touch the underlying component (Object would probably serve the same purpose). See how we are allowed to access properties of a List<void> but not properties of the elements:

$ cat g.dart
void main() {
  var c = <String>["one", "two", "three"];
  fn(c);
  fn2(c);
}

int fn(List<void> list) => list.length;

int fn2(List<void> list) => list.first.length;
$ dart --preview-dart-2 g.dart
g.dart:9:40: Error: The getter 'length' isn't defined for the class 'void'.
Try correcting the name to the name of an existing getter, or defining a getter or field named 'length'.
int fn2(List<void> list) => list.first.length;
                                       ^
like image 146
Sam Rawlins Avatar answered Dec 16 '25 23:12

Sam Rawlins



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!