Why is dart wrongly inferring my generic parameter type?

Viewed 305

I can't seem to understand why the mySet variable is being inferred as a generic set with dynamic parameter type, when I clearly equated it to an int set literal. So is this result logical or has dart genuinely failed to infer the generic sets parameter type?

main(){
  Set mySet = {1 ,2 , 3};
  var myProducts = {
    1:'TV',2:'Refrigerator',
    3:mySet.lookup(2),
    4:'Tablet',
    5:'Computer'
    };
    var userCollection = {"name":"John Smith","Email":"john@sanjib.site"};
    myProducts.forEach((x,y) => print("${x} : ${y}"));
    userCollection.forEach((k, v) => print("${k} : ${v}"));
}
1 Answers

If you declare a variable with a generic type Generic but omit the parameterized type, it's (usually1) shorthand for Generic<dynamic>. Inference does happen in your assignment, but not in the direction you expect: since you explicitly declared the type of mySet, the type of {1, 2, 3} is inferred from that explicit type (Set/Set<dynamic>) and becomes <dynamic>{1, 2, 3} instead of <int>{1, 2, 3}.

This is one reason why it can be better to omit explicit types and just allow types to be inferred when possible. Using:

var mySet = {1, 2, 3};

would allow mySet to be inferred as Set<int>. If you really want to use explicit types, you should ensure that you specify explicit types for generic type parameters too.

You can catch such errors by setting:

analyzer:
  language:
    strict-raw-types: true

in your analysis_options.yaml file. (You also might wish to enable:

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false

too.)

Also see https://github.com/dart-lang/language/blob/master/resources/type-system/strict-raw-types.md:

void main() {
  List a = [1, 2, 3];
}

Developers often think that inference fills in the type of a from the right side of the assignment. It may look like a has the type List<int>. But Dart fills in omitted type arguments, like E on List, with dynamic (or the corresponding type parameter's bound); List a; is purely a shorthand for List<dynamic> a;. Inference then flows from a onto the expression on the right side of the assignment.


1 Strictly speaking, if a generic's type parameter is constrained (e.g. class Generic<T extends Base>), then omitting the type parameter is shorthand for using that constraint (i.e., Generic would be shorthand for Generic<Base>). Most generics don't constraint their type parameters, so therefore it usually ends up being dynamic.

Related