-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Suggested code from omit_local_variable_types linter fails at runtime #58201
Comments
This also happens with functions like void f() {
List x = List.filled(10, null);
print(x.runtimeType);
}
void f() {
var x = List.filled(10, null);
print(x.runtimeType);
x.add(123); // compile time type error
} |
I think this issue fits better as a linter issue, so I'm transferring it there. The proposal would then be that |
I believe the recommended style is to use If that's the case, then this becomes an issue with the fix, rather than with the lint. |
That recommendation has indeed been put forward several times. However, we might note that this is to some extent a recommendation to avoid type inference, because it requires the developer to provide feed-forward type arguments in various locations in the initializing expression, whereas the variable with an explicit type annotation (that is I usually push the idea that |
That's right, and the "Effective Dart" rule for it links to this lint.
That's what we say for fields and top-level variables. For locals, we felt it was better to have a simple blanket rule that can be applied mechanically. Since a local variable never bleeds out into a library's exported API, pinning down the precise type is less important. |
@munificent wrote:
Of course, all these considerations are not relevant to Otherwise, the reason why I keep pushing for an My main reason is that the placement of the type information as a declared type is a declarative approach that relies on type inference to obtain the actual type arguments at various locations in the initializing expression. So we specify the desired end result, and ask the computer to sort out all the details in order to reach that result. In contrast, the placement of one or more actual type arguments in the initializing expression requires the developer to (1) sort out all those details themselves (in order to know which type arguments to provide, and where), and (2) redo the information propagation in order to compute the actual declared type of the variable whenever they need to read and understand the code. In general, I'd consider "state the desired end result and let the computer sort out the details" to be a more rational and useful approach than "sort out the details manually, write some information that will produce the end result after any number of complex steps that aren't visible in the source code, and ask the developers who will ever need to read and understand this code to redo those steps manually". |
I definitely see the appeal of that as a consistent approach. Basically, there's two options to avoid users redundantly specifying information:
For top-level variables and fields we encourage users to do (1) because we want them to think about the type signatures of their API explicitly since a change to those types can have far-reaching effects. For local variables, we lean towards (2) because expressions carry type information that can be inferred from values. In: var x = [1]; They don't have to write Users really like this kind of inference, so we want them to use it when they can. But some initializing expressions don't carry enough information to infer all type arguments: var x = []; That leaves us with two options: var x = <int>[];
List<int> x = []; We went with the former because that leads to more consistent code: var x = [1];
var y = <int>[];
var z = [3]; Versus: var x = [1];
List<int> y;
var z = [3]; In the latter example, There are still times when you need to annotate a local variable, if the inferred type is not what you want or there is no initializer. But those are rare and stand out deliberately. Philosophically, I think there's an argument to prefer specifying information in expressions and inferring types from them because expressions are usually more precise than types since they describe an actual value. |
@munificent wrote:
I created #58773 in order to have the outline of a proposal for how to milden Here's the effect on the examples: var x = [1]; The type of var x = []; The type of
var x = <int>[];
List<int> x = []; The type of You could say that the type of
var x = [1];
var y = <int>[];
var z = [3];
var x = [1];
List<int> y = [];
var z = [3]; With #58773, both forms would be accepted. However, I'm interested in cases where the type of the initializing expression is not obvious, so the really interesting case is more like this, where the type of var x = someReceiver.someFunction<int>(on, some, arguments); In this case I'd very much prefer to allow the developer to proceed if they want to write this: String x = someReceiver.someFunction<int>(on, some, arguments); The type So if we update the version of the package where |
Take this example:
This code works as expected, but the
omit_local_variable_types
linter doesn't like it becausea
has an explicit type. In order to fix this, I used Visual Studio Code's "Quick Fix" tool, which changed the contents ofmain
to the following:Notice the loss of type information. The linter is happy with this change, but when I run the code, it throws the following exception:
Which makes complete sense, given that it's trying to allocate an unknown type of native object. My best guess of what it was supposed to have suggested is this:
var a = allocate<MyStruct>()
.Dart VM version: 2.8.4 (stable) (Wed Jun 3 12:26:04 2020 +0200) on "macos_x64"
Tested on macOS and Windows.
The text was updated successfully, but these errors were encountered: