7

The upcoming Dart 2.X release requires strong typing. When working with JSON data we must now cast dynamic types to an appropriate Dart type (not a problem). A related question Ignoring cast fail from JSArray to List<String> provides the answer to use the .cast<String>() function. Also a recent group messages says the same: Breaking Change: --preview-dart-2 turned on by default.

The problem is that the .cast() function doesn't seem to compose. This original code when compiled using DDC and run in the Chrome browser:

Map<String, dynamic> json = { "data": ["a", "b", "c"] };
List<String> origBroken = json["data"].map( (s) => s.toUpperCase() ).toList();

Now receives the runtime warning (which will soon be an error)

Ignoring cast fail from JSArray to List<String>

So I add the .cast<String>() as the documentation and related link suggest and still receive the warning:

List<String> docFixBroken = json["data"].cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> alsoBroken = List.from( (json["data"] as List).cast<String>() ).map( (s) => s.toUpperCase() ).toList();

The code that doesn't give the warning requires a temporary variable (and also seems to be able to skip the explicit cast):

List<String> temp = json["data"];
List<String> works = temp.map( (s) => s.toUpperCase() ).toList();

So how can I write the cast and map as a single composed expression? The reason I need it as a single expression is that this expression is being used in an initializer list to set a final class variable.

Richard Johnson
  • 502
  • 1
  • 6
  • 14

1 Answers1

13

I wrote Ignoring cast fail from JSArray to List<String>, so let me try and help here too!

So I add the .cast<String>() as the documentation and related link suggest and still receive the warning:

List<String> docFixBroken = json["data"].cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> alsoBroken = List.from( (json["data"] as List).cast<String>() ).map( (s) => s.toUpperCase() ).toList();

Unfortunately, List.from does not persist type information, due to the lack of generic types for factory constructors (https://github.com/dart-lang/sdk/issues/26391). Until then, you should/could use .toList() instead:

(json['data'] as List).toList()

So, rewriting your examples:

List<String> docFixBroken = json["data"].cast<String>().map( (s) => s.toUpperCase() ).toList();
List<String> alsoBroken = List.from( (json["data"] as List).cast<String>() ).map( (s) => s.toUpperCase() ).toList();

Can be written as:

List<String> notBroken = (json['data'] as List).cast<String>().map((s) => s.toUpperCase()).toList();

Hope that helps!

Giacomo M
  • 4,450
  • 7
  • 28
  • 57
matanlurey
  • 8,096
  • 3
  • 38
  • 46
  • Thanks that works when I corrected a typo by adding "().map" after the ".cast". – Richard Johnson Mar 28 '18 at 17:17
  • I played with your `.toList()` solution and found that I needed to cast `as List` not just `as List`. So this also works `(json["data"] as List).toList().map(...` – Richard Johnson Mar 28 '18 at 17:44
  • Specifying the type to the mapping function parameter allows the `as List` to work `(json["data"] as List).toList().map( (String s) => s.toUpperCase() ).toList();` – Richard Johnson Mar 28 '18 at 17:55