In my question Dart 2.X List.cast() does not compose the answer requires converting a List<dynamic>
to a List<String>
as such:
List<String> ls = (json['data'] as List).cast<String>().map((s) => s.toUpperCase()).toList();
My experience from other languages had me write this first:
List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();
Note that this compiles but fails at runtime in Dart 2.
Why does Dart typecasting for a List
require a function as List).cast<String>()
as opposed to simply using the Dart as
"typecast operator" such as as List<String>
?
---- Edit ----
I am using the most recent Dart 2.0.0-dev.43.0 and get inconsistent runtime behavior with as
typecasts/assertions. Isn't the .cast<>()
function creating a new iterable the same as a .map()
? Changing my code to this works:
List<String> ls = (json['data'] as List).map((s) => (s as String).toUpperCase()).toList();
This seems to take advantage that the first cast to List
is a List<dynamic>
. Thus the .map
function parameter is also a dynamic
.
My second example above with to as List<String>
works in some places in our code but not others. Note that IntelliJ correctly infers the types in all of the above examples - it's the runtime where the failure happens. I'm guessing that the inconsistent behavior is due to Dart 2.x being still in development.
---- 2nd Edit ----
Here are my test cases that I have in one of my class constructors:
Map<String, dynamic> json = { "data": ["a", "b", "c"] };
//List<String> origBroken = json["data"].map( (s) => s.toUpperCase() ).toList();
// Sometimes works - sometimes gives "Ignoring cast fail from JSArray to List<String>" at runtime!!!
List<String> wonky = (json["data"] as List<String>).map( (s) => s.toUpperCase() ).toList();
print("Wonky $wonky");
List<String> fix1 = (json["data"] as List).cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> fix2 = (json["data"] as List).map( (s) => (s as String).toUpperCase() ).toList();
List<String> explicit2 = (json["data"] as List<dynamic>).map( (dynamic s) => (s as String).toUpperCase() ).toList();
// From accepted answer of the linked question - compile error because .cast() doesn't take parameters
// error: Too many positional arguments: 0 expected, but 1 found.
//List<String> notBroken = (json['data'] as List).cast<String>((s) => s.toUpperCase()).toList();
List<String> notBrokenFixed = (json['data'] as List<String>).cast<String>().map((String s) => s.toUpperCase()).toList();
The problem is the warning Ignoring cast fail from JSArray to List<String>
sometimes given by the wonky
assignment. When I say sometimes it's because it changes unpredictably as I make changes to the main application that uses the library that contains this code - without making changes to this class or even the library.
At the time I wrote the first edit above, wonky
wasn't working. I just tried it again now and it's working. I have not changed any code in this library - I have been working in the main application which has a dependency on this code's library.
Some background, this is a multi-library project being converted from Angular/Typescript. These test cases are based on the processing we do to deserialize JSON into Dart classes. We map JSON (dynamic) strings into various data structures such as enums, Option<> and Either<> (from dartz) using class constructor initializers.
A couple weeks ago the runtime warning started happening I believe because of Breaking Change: --preview-dart-2 turned on by default.
I understand that this warning will soon be an error. So I traced the warning back to these conversions that map from JSON dynamic data (Yes, dynamic data is an edge case in Dart but it's what dart:convert
provides).
We are developing on Mac using DDC with the most recent Dart 2.0.0-dev.43.0, angular 5.0.0-alpha+8, build_runner 0.8.0, IntelliJ 2018.1 and running on Chrome 65.0.3325.181.
---- Final Edit ----
There is an instability in the current development build/runtime that is behind this issue. No, I don't have a reproducible example. Changing and rebuilding our main app will cause this code in an unmodified library dependency to sometimes give the runtime warning Ignoring cast fail from JSArray to List<String>
.
The suspect code from the original part of this question (also wonky
above)
List<String> ls = (json['data'] as List<String>).map((s) => s.toUpperCase()).toList();
casts the dynamic JSON data to a List<String>
. The types are fully constrained and the Dart analyzer/IntelliJ infers s
to be Static type: String
.
The runtime warning that sometimes occurs and related answers to use .cast()
is what led to this question. At this time I'll believe the analyzer and ignore the runtime warning.