Type inference
The analyzer can infer types for fields, methods, local variables, and most generic type arguments. When the analyzer doesn’t have enough information to infer a specific type, it uses the dynamic
type.
Here’s an example of how type inference works with generics. In this example, a variable named arguments
holds a map that pairs string keys with values of various types.
If you explicitly type the variable, you might write this:
Map<String, dynamic> arguments = {'argA': 'hello', 'argB': 42};
Alternatively, you can use var and let Dart infer the type:
var arguments = {'argA': 'hello', 'argB': 42}; // Map<String, Object>
The map literal infers its type from its entries, and then the variable infers its type from the map literal’s type. In this map, the keys are both strings, but the values have different types (String
and int
, which have the upper bound Object
). So the map literal has the type Map<String, Object>
, and so does the arguments
variable.
Field and method inference
A field or method that has no specified type and that overrides a field or method from the superclass, inherits the type of the superclass method or field.
A field that does not have a declared or inherited type but that is declared with an initial value, gets an inferred type based on the initial value.
Static field inference
Static fields and variables get their types inferred from their initializer. Note that inference fails if it encounters a cycle (that is, inferring a type for the variable depends on knowing the type of that variable).
Local variable inference
Local variable types are inferred from their initializer, if any. Subsequent assignments are not taken into account. This may mean that too precise a type may be inferred. If so, you can add a type annotation.
var x = 3; // x is inferred as an int x = 4.0;
num y = 3; // a num can be double or int y = 4.0;
Type argument inference
Type arguments to constructor calls and generic method invocations are inferred based on a combination of downward information from the context of occurrence, and upward information from the arguments to the constructor or generic method. If inference is not doing what you want or expect, you can always explicitly specify the type arguments.
// Inferred as if you wrote <int>[].
List<int> listOfInt = [];
// Inferred as if you wrote <double>[3.0].
var listOfDouble = [3.0];
// Inferred as Iterable<int>
var ints = listOfDouble.map((x) => x.toInt());
In the last example, x
is inferred as double
using downward information. The return type of the closure is inferred as int using upward information. Dart uses this return type as upward information when inferring the map()
method’s type argument: <int>
.