Comments (11)
It's not documented in the language tour, but it is mentioned in other places, for example the style guide:
Avoid writing incomplete generic types.
It is surprising and annoying, and if it wasn't for the historical reasons, it probably wouldn't be like that.
The fact that writing:
final foo = {"a": 1};
final Map foo = {"a": 1};
final Map<String, int> foo = {"a": 1};
final foo = <String, int>{"a": 1};
final Map foo = <String, int>{"a": 1};
final Map<String, int> foo = <String, int>{"a": 1};
gives different results for just the Map
-typed ones is annoying, it looks like a place where inference could supply the type arguments. It isn't, for numerous reasons (Dart only infers type arguments in invocations ... and now patterns, but not in types).
And then it's doubly annoying that it defaults to dynamic
instead of Object?
, for said historical reasons.
But until we manage to change that, it is what you have to work with.
The strict-raw-types
is a good suggestion. There shouldn't be any situation where you need to write a raw type. If you want Map<dynamic, dynamic>
you can still write it explicitly.
from sdk.
You probably just want to avoid using a raw type like Map
. Make it final Map<String, Object?> map
, and there is no run-time error.
You can enable strict-raw-types
and various other static analysis options in order to get a more strict analysis. Here's an example analysis_options.yaml
:
analyzer:
language:
strict-raw-types: true
strict-inference: true
strict-casts: true
linter:
rules:
- avoid_dynamic_calls
The raw type Map
used as a type annotation (for example, as the declared type of a variable) has a specific meaning: It means Map<dynamic, dynamic>
.
In other situations you can have type inference, e.g., Iterable<int> xs = List.filled(5, 0);
will cause type inference to make it List<int>.filled(5, 0)
, but when you are referring to a generic class just by name (no actual type arguments) and using it as a type annotation, the type arguments are considered to be unknown and they will be chosen based on the declared bounds (and when there are no bounds, like in Map
, the chosen value is dynamic
).
So map
has type Map<dynamic, dynamic>
, and this has some consequences. In particular, map['hello']
has type dynamic
. (So you don't need ?.
, a plain .
will do, but the invocation of a member is checked at run time and everything is accepted without checks at compile time).
In particular, toBool
is assumed to be an instance member of the run-time type of map['hello']
. Given that there "is" an instance member, extension members are not considered. (When we have both, the instance member always wins.)
So we're calling toBool
on a String
in map['name']?.toBool()
and getting a run-time error because String
does not have a member named toBool
.
from sdk.
@eernstg In some large codebases like ours, we can't enable these analyzer hints because we rely on raw types in a few areas. This includes raw-types on a modified implementation of json_serializable factory functions.
The root of this issue is that the base type of a map retrieval operation is Object?, and yet the dart SDK seems to be ignoring it in this specific scenario. Your solution is offering a workaround on a potentially bigger problem.
from sdk.
Yeah explicitly casting it to Object?
works and doesn't throw but if it can cast it to Object?
than it should be able to call an extension on Object?
too since it would be its super type, unless I am misunderstanding something! Can you confirm whether this would've worked before? We had this code in our codebase and it was working fine until now. I can't confirm the last working version of Dart SDK though.
My reference:
https://dart.dev/null-safety/understanding-null-safety#top-and-bottom
from sdk.
The raw type instantiates to bounds, which means dynamic
, and extension methods do not work on dynamic
.
Instantiate to bounds works like that for historical reasons (it was a way to make the Dart 2.0 type system viable, without breaking too much existing code that relied on things being dynamic
.)
If you do:
final Map foo = { ... };
you are effectively writing
final Map<dynamic, dynamic> foo = { ... };
which means that foo['hello']
has type dynamic
, and you can't call extension methods on it.
Changing it to final Map<String, Object?> foo = {...};
or just final foo = {...};
will avoid the raw type that gets instantiated to bounds, which makes foo['hello']
have type Object?
, and then the extension method works.
I'm all for changing what instantiate to bounds do, but that's a larger breaking change, and won't help you any time soon.
from sdk.
@lrhn It seems counter-intuitive for the untyped version to default to Map<..., dynamic>
while the inferred type to defaults to Map<..., Object?>
, no? It's a direct and unexpected discrepancy to a developer.
This is not documented behavior even.
from sdk.
we rely on raw types in a few areas
You can turn off strict-raw-types
by having some exclude
clauses in your analysis_options.yaml
or by putting // ignore: strict_raw_type
just before the affected lines, or // ignore_for_file: strict_raw_type
in the affected libraries. You could also pass dynamic
as an actual type argument explicitly. So you do have a number of ways to enable this warning and still disable it in some locations.
base type of a map retrieval operation is
Object?
, and yet the dart SDK seems to be ignoring it in this specific scenario.
The return type of operator []
on Map<K, V>
is V?
. It will be dynamic?
(which is immediately normalized to dynamic
, which doesn't matter) when the actual type argument passed to V
is dynamic
.
So there's nothing exceptional about the type of map['hello']
in this case.
from sdk.
if it can cast it to
Object?
than it should be able to call an extension onObject?
too
As I mentioned here, dynamic
is assumed to have all instance members, with all possible signatures, and (even assumed) instance members have a higher priority than extension members.
The assumption of "has all members" is not a subtype based property, this is a special affordance which is given to the type dynamic
, such that developers can choose (by having a receiver of type dynamic
) to perform member invocations that are not statically type safe, and only check at run time that the given member actually exists and that it accepts the given kind of invocation (e.g., that it accepts that many parameters, of those types, etc.).
The consequence, as @lrhn mentioned here, is that
extension methods do not work on
dynamic
.
You will simply never be able to execute any extension methods when the static type of the receiver is dynamic
.
It is true that dynamic
is a subtype of Object?
(and vice versa), but because of this special "it has all members" assumption it is not true that "it should be able to call an extension on Object?
".
from sdk.
This is not documented behavior even.
If you want the gory details then check out the section 'The instantiation to bound algorithm' in the language specification. The choice of dynamic
when there is no bound is found at the end of the first paragraph in that section.
from sdk.
We had this code in our codebase and it was working fine until now.
Nothing has changed about the fact that raw type annotations use instantiation to bound, and instantiation to bound uses dynamic
when no bound is declared for the corresponding type variable. I cannot see how it could be possible that a receiver of type dynamic
has an extension member invoked on it, nor how map[...]
could have any other type than dynamic
when map
has type Map<dynamic, dynamic>
. So I really don't know how that could work, with any version of Dart...
from sdk.
Agree. This seems to be working exactly as intended (or at least as currently specified).
Until we get rid of the currently specified behavior, there is no bug, and the only surprising thing here is that it used to work.
from sdk.
Related Issues (20)
- Implement augmentations HOT 12
- Support macros when analyzer is used as a library HOT 5
- Inability to Convert Dart BigInt to JSBigInt using new js_interop HOT 1
- Dart CLI commands hang on a slow/unstable internet connection due to analytics HOT 8
- Types Generated using the LibraryTypesMacro throws an error when instantiating HOT 2
- Analyzer Feedback from IntelliJ HOT 1
- Doc comment references cannot resolve an extension member when prefixed by the extended type's name HOT 11
- Different augmented output from macro during development and program running
- pkg/dds/test/devtools_server/instance_reuse_test timing out HOT 4
- [Bug] Uri.parse failes to currectly parse url like: http://user:^@example.com HOT 5
- string.allMatches returns an Iterable of length pattern.length + 1 when aString is empty HOT 6
- [CP] Pub: Fix to using path dependencies in git dependencies on windows HOT 2
- The analyzer encounters an unhandled exception during constant verification in a switch statement HOT 1
- Erasure of extension types in DartObject is blocking adoption of dart:js_interop HOT 8
- `IOSink.flush()` appears to be able to end a Dart program immediately HOT 3
- Give warning for probable wrong inclusion of `=>` in block lambdas: `() => { foo(); }` HOT 4
- Invalid `main` accepted by the front end HOT 5
- macro language tests are failing on new core libraries addition HOT 1
- DartDoc comment of `spawnUri` needs updates HOT 3
- File copying on Windows buildbots is broken HOT 10
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from sdk.