In Dart, how does var infer a local variable's type, and what happens when a var declaration has no initializer?
answer
- inferred once, from the initializer
- later assignments ignored
- annotate the wider type
- no initializer means dynamic
- prefer_typing_uninitialized_variables
basics
~20 sIn Dart, var takes its static type from the initializer, once: var miles = 3 makes miles an int for good, so assigning 2.5 later is a compile error. A var with no initializer gets dynamic, which switches off type checking.
solid answer
~40 s`var` is not a dynamic type; it asks the analyzer to **infer** the static type from the initializer. `var miles = 3;` makes `miles` an `int`, and later assignments are not taken into account, so `miles = 2.5;` fails. When you need a wider type, annotate it: `num distance = 3; distance = 2.5;` works. `final` and `const` infer the same way. If a `var` has **no initializer**, inference has nothing to go on and the variable becomes `dynamic`: any assignment is accepted and member calls are only checked at runtime, where they can throw `NoSuchMethodError`. Effective Dart therefore says to use `var` or `final` for initialized locals without writing the type, but to annotate variables declared without an initializer; the `prefer_typing_uninitialized_variables` lint in the core set enforces that.
code
dart · 12 linesvoid main() {
var miles = 3; // inferred int
// miles = 2.5; // error: double can't be assigned to int
num kilometers = 5; // annotate the wider type you need
kilometers = 8.05;
var unit; // no initializer: static type is dynamic
unit = 'km';
unit = 42; // accepted: dynamic disables checking
print('$miles $kilometers');
print(unit.toUpperCase()); // compiles, throws NoSuchMethodError at runtime
}go deeper
Recall that var infers the type from the initializer and that the type then stays fixed.
Explain why an uninitialized var becomes dynamic, how to widen with an annotation, and the Effective Dart rules on when to annotate.
Enforce the uninitialized-variable lint and annotate public top-level values so inference changes never alter an API silently.
Agree team rules on inference versus annotation that keep code readable without hiding dynamic types.
## var means "infer", not "anything" Dart is statically typed with **type inference**. Writing `var` asks the analyzer to work out the variable's static type from its initializer, exactly as if you had written it: ```dart var miles = 3; // int var name = 'mile'; // String var factors = {'mi': 1609.344, 'ft': 0.3048}; // Map<String, double> ``` After that the variable has a fixed static type. It is not a JavaScript-style variable that can hold any kind of value: in JavaScript, `var` only declares a name, while in Dart it declares a name *and* fixes its type from the initializer. ## Inference happens once dart.dev's type-system page states the rule for locals: types are inferred from the initializer, if any, and **subsequent assignments are not taken into account**. That can infer a narrower type than you meant: ```dart var distance = 3; // int distance = 4.0; // error: a double can't be assigned to an int ``` The fix is an annotation with the type you actually want: ```dart num distance = 3; distance = 4.0; // fine ``` `final` and `const` infer the same way: `final loadedAt = DateTime.now();` is a `DateTime`. ## No initializer, no inference If the declaration has no initializer, there is nothing to infer from. Effective Dart explains that when inference lacks information, Dart usually **fills in `dynamic` silently**, and that implicit `dynamic` looks safe but disables type checking. So: ```dart var unit; // static type: dynamic unit = 'km'; unit = 42; // accepted unit.toUpperCase(); // compiles; throws NoSuchMethodError at runtime ``` That is why Effective Dart has the rule **"DO type annotate variables without initializers"**, backed by the `prefer_typing_uninitialized_variables` lint, which sits in the core, recommended and flutter lint sets. With an annotation, flow analysis still lets you assign later, as long as every path assigns before use: ```dart double factor; if (unit == 'mi') { factor = 1609.344; } else { factor = 1.0; } ``` ## Where inference applies | Declaration | Type comes from | |---|---| | Local `var`/`final` with initializer | The initializer | | Local `var` without initializer | Nothing: `dynamic` | | Top-level or static variable | Its initializer | | Instance field overriding a superclass member | The inherited type | | Instance field with an initializer | The initializer | Top-level inference has one more failure mode: it fails if the type depends on itself through a cycle. ## Common mistakes 1. Declaring `var result;` and assigning values of several types: the variable is silently `dynamic`, and errors move from the analyzer to runtime. 2. Writing `var distance = 3;` and later needing fractions: write `double distance = 3;` or `var distance = 3.0;` instead. 3. Writing `dynamic` explicitly where `var` would infer a precise type: it throws away checking for no benefit. 4. Believing `var` is weaker than an explicit type: once inferred, the type is enforced exactly as if it had been written. The unifying idea is that inference is a convenience for **writing** types, never a switch to dynamic typing, except in the one case where there is nothing to infer from. ## Style: when to write the type Effective Dart's summary is short: - **Don't** annotate initialized local variables; `var` or `final` is enough. - **Do** annotate when inference lacks context, including uninitialized variables. - **Prefer** annotating top-level variables and fields unless the initializer makes the type obvious. In a unit-conversion library that means `final factor = metersPerMile / metersPerFoot;` inside a function, but explicit types on public top-level values, where the annotation documents the API. Note that `const double metersPerKilometer = 1000;` is fine: an integer literal in a `double` context becomes a `double`.
- What type does Dart infer for var factors = {'mi': 1609.344, 'ft': 0.3048};?`Map<String, double>`. The map literal infers its type from its entries, and the variable takes the literal's type. So `factors['yd'] = 1;` still works, because the literal becomes a `double` in that context, but assigning an `int` variable does not compile.
- Is var ever a problem for public top-level declarations in Dart?It works, but Effective Dart prefers annotating top-level variables and fields unless the initializer makes the type obvious. The annotation documents the API and stops an initializer change from silently changing the public type.
saying these in an interview costs you the question
- var in Dart makes a variable dynamic, like JavaScript's var
- Dart re-infers a var's type from each later assignment
- A var with no initializer takes the type of its first assignment
- Every local variable should carry an explicit type annotation
- Assigning a double to a var initialized with 3 widens it to num