Search code examples
dartnullablestatic-analysis

Why is asigning a dynamic to a non-nullable not an error with sound null safety


In dart with sound null safety turned on it is entirely possible to do

dynamic myVar; // myVar assumes default value of null
String someString = myVar; // No warning by IDE.

As expected the above results in a run-time error since myVar is null and someString is non-nullable.

Is this a problem with the linter/IDE?

I did discover that I can enable pedantic linting that causes the IDE to show a warning when I try to implicitly cast dynamic to another type. Turning that on helps but I think the problem is that dynamic can be null without having to be explicitly defined as nullable.

In short we don't have

dynamic? myNullableVar;

My questions is: Is this a bug?

This "issue" bites you most commonly when you do something like

Map<String, dynamic> myData = jsonDecode(receivedResponseBody);
var name = myData['name'];

In the above example I expect the IDE to show a warning that I am trying to assign a potentially null value to a non-nullable variable. The IDE should require the user to add a null check.

If this is not a bug (in the IDE or in the linter), then why not?

Long story short: The implicit cast from dynamic to another type masks the issue with null assignments, and I expect the IDE to provide a warning.


EDIT: after note from @ jamesdlin below Specifically I am OK with the following where the Left-hand side of the assignment allows null values.

dynamic someVar;
String? myString = someVar;

Side note, I was hoping that the new dart typeDef feature would allow my to build something similar to Union type hints in Python. That would allow me to then get something like

typeDev JsonValueType = { int, String, float, bool };

And the result from jsonDecode would then be

Map<String, JsonValueType?>

Which explicitly includes Null and therefore the IDE would warn the user to add a null check.

My opinion: As long as you can assign any nullable type on the right of an assignment operator to a non-nullable type on the left, you don't have true sound null safety.


Solution

  • It's not a bug.

    You can do:

    dynamic x = "not an int";
    int y = x;
    

    and not get any compiler warnings. That's just how dynamic works, it turns off compiler ("static") type-checks and relies on run-time ("dynamic") checks instead. That's where the name comes from.

    There is no reason to allow dynamic? because dynamic already allows the null value (and any other value). The dynamic type is inherently nullable, adding ? to it makes no difference, just as Null? is meaningless.

    The Dart type system is statically null sound (mostly, the usual caveats around generics apply), but dynamic is a way to turn that type system off on request. Don't use dynamic unless you want that effect.