Avoid type explicit type annotations when they can be inferred.
From the style guide:
If an invocation鈥檚 type argument list is correctly inferred with the types you want, then omit the types and let Dart do the work for you.
GOOD:
var strings = ['foo', 'bar', 'baz'];
var fakeSimStore = FakeStore(Sim());
var stars = Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.black),
],
);
BAD:
var strings = <String>['foo', 'bar', 'baz'];
var fakeSimStore = FakeStore<Sim>(Sim());
var stars = Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.black),
],
);
/cc @jefflim-google
/cc @goderbauer @Hixie @a14n (flutter)
/cc @munificent (style)
/cc @davidmorgan (pedantic)
/fyi @scheglov (analyzer API implications)
Shouldn't fakeSimStore examples be inversed?
I don't have a problem with this lint existing, but I would strongly object to turning it on by default.
I think it makes intent harder to discern. For example, maybe you want to return a List<Foo> and it happens that bar() and baz are of type Foo, but that's not obvious:
return [bar(), baz]; // what's the type of the list?
Contrast:
return <Foo>[bar(), baz];
A similar problem exists with other types. For example:
var foo = Bar(baz()); // what's the type argument to the Bar constructor?
Contrast:
var foo = Bar<Quux>(baz());
I think it makes certain patterns ugly. For example, suppose you have three lists of nums, if any of them accidentally infer the wrong type then that one has to be explicitly stated:
var a = [3, 1.5];
var b = [1.5, 0];
var c = <num>[0]; // have to say <num>[] otherwise it would be a List<int>
Contrast:
var a = <num>[3, 1.5];
var b = <num>[1.5, 0];
var c = <num>[0];
Shouldn't fakeSimStore examples be inversed?
馃槵
Ummmm. Yeah. Thanks! 馃う
I am with Hixie: In most of these cases I like the added clarity that those explicit types bring to the table. I am not against the existence of the lint, I hope it wouldn't be forced upon me, though :)
Thanks everyone!
I suspect there is room for a enforceable (in google3/pedantic) lint here.
Note that in google3 we have already enforced omit_local_variable_types, which is very much along these lines. What we found is that it removes a _lot_ of noise from the code. There were a very small number of _general_ objections to the lint, but I haven't had any reports of specific cases where it caused problems, and no requests to relax the lint after it was enforced.
I suspect we will also enforce avoid_types_on_closure_parameters although IIRC that needs a few tweaks first.
The argument for both of those is that the important places where you explicitly specify types are in _api_ not _implementation_. E.g. the type of the thing you return is the method's return type, you don't need to also specify it in the implementation.
As usual for google3 we'll evaluate based on pre-existing violations and how the code would change to see if this looks right in practice.
Thanks :)
Maybe you could consider a lint that flags cases where the declared type is non-trivial? In particular, it seems directly counterproductive if developers are punished for documenting the intended element type of a list literal with many elements of different types, just because the desired type argument T is exactly the least upper bound of those many types. Months later one of those elements gets deleted, the least upper bound is now a subtype S of what it was previously, and we get a type error when we try to add an object that is typable as a T but not as an S (and that's a run-time error if the list has been assigned to a variable or parameter with type List<T> on the way to that situation).
The notion of being trivial can be defined in many ways, of course, but for collection literals it could be: _A collection literal has a non-trivial element type if it contains elements (for maps: key/value pairs) that do not have the same static type._
This means that it's allowed (but not required) to specify the element type in the cases where relying on inference is obviously error prone.
Thanks Erik. In general I agree there's room for weakening the lint for collection literals along the lines you suggest--need to figure out if that looks more or less confusing in the end :)
Does this lint rule apply to fields and top level variables as well? I'm 1000% excited about this for local variables and type arguments, but for fields and top level variables, we do support users annotating those explicitly even when the annotation is redundant.
Does this lint rule apply to fields and top level variables as well?
I think it's all very much open to discussion and really, we should just define it to be as aligned w/ "canonical" style. Maybe we could add a clarifying note in the style guide?
I think Effective Dart is already pretty clear. (Especially now that I've rewritten the type guidelines a couple of months ago.) It has separate rules for type arguments, locals, and other declarations:
DON鈥橳 write type arguments on generic invocations that are inferred.
DO type annotate fields and top-level variables if the type isn鈥檛 obvious.
DON鈥橳 redundantly type annotate initialized local variables.
So, according to the guidelines, locals and type arguments should never be annotated redundantly, but fields and top levels can if you feel that the type isn't "obvious", which it's deliberately pretty vague about.
Maybe split it into two lints?
I'm on the same page.
So, according to the guidelines, locals and type arguments should never be annotated redundantly, but fields and top levels can if you feel that the type isn't "obvious", which it's deliberately pretty vague about.
馃挴
This is exactly what I'd propose unnecessary_type_annotations enforce: locals and type args, skipping fields and top level variables entirely.
/fyi @jefflim-google
This is exactly what I'd propose
unnecessary_type_annotationsenforce: locals and type args, skipping fields and top level variables entirely.
馃挴馃挴馃挴馃挴馃挴
I would maybe consider putting "local" in the lint name too (because I could see some users potentially wanting a separate lint that does the same for fields and top level variables).