Dog? dog;
bool isDog = dog is Dog;
if (isDog) {
dog.bark();
}
i.e. boolean variables which serve as witnesses for type judgements are integrated into promotion machinery. Dog? maybeDog;
if (maybeDog != null) {
maybeDog.bark();
}
If compiler says "sorry, maybeDog might be null", developer (rightfully so) usually responds "but I have just checked and I know it is not, why do you bother me?". So languages chose to accommodate this. Dog? maybeDog;
if (maybeDog != null) {
maybeDog.bark();
maybeDog = null;
}
> Why should I have to wrap things back in an optional if I want to pass it along as such? void foo(Dog? maybeDog);
Dog? maybeDog;
if (maybeDog != null) {
maybeDog.bark();
foo(maybeDog); // Dog can be used where Dog? is expected
}
You might need to account for it in places where type inference infers type which is too precise, e.g. Dog? maybeDog;
if (maybeDog != null) {
// This will be List<Dog> rather than List<Dog?>.
final listOfDogs = [maybeDog];
}
Though I don't think it is that bad of a problem in practice. if (value case final value?) {
// value is a fresh final variable
} sealed class MySomething<T> {
}
final class One extends MySomething<Never> {
}
final class Two extends MySomething<Never> {
final Child child;
Two(this.child);
}
final class Three<T> extends MySomething<T> {
final T value;
Three(this.value);
}
final class Four extends MySomething<Never> {
final int Function() callback;
Four(this.callback);
}
And then you can exhaustively switch over values of MySomething: int foo(MySomething<String> foo) {
return switch (foo) {
One() => 1,
Two(child: Child(:final value)) => value,
Three(:final value) => int.parse(value),
Four(:final callback) => callback(),
};
}
The declaration of a sealed family is considerably more verbose than Swift - and I really would like[1] us to optimized things for the case when people often declare such families. Macros and a primary constructors will likely provide reprieve from this verbosity. sealed class Message();
class IncrementBy(final int value) extends Message;
class DecrementBy(final int value) extends Message;
class Set(final int value) extends Message;
Which is considerably less repetitive, though `extends Message` is still there. I am fairly optimistic that the next step would be to eliminate that[1], though I think we would need to gather a bit more feedback from users as people are getting more and more reps in with Dart 3.0 features. I personally would prefer something along the lines of: sealed class Message() {
case IncrementBy(final int value);
case DecrementBy(final int value);
case Set(final int value);
}
Current syntax is not all that bad if you are going to do OO and add various helper methods on `Message` and its subclasses, but if you just want to define your data and no behavior / helpers - then it is exceedingly verbose.
Dart 2.12 (released March 2021) introduced null-safety.
That started a 2 year transitionary period during which you could mix nullsafe (language versions 2.12 or above) and non-nullsafe (language versions below 2.12) code in one program.
Dart 3.0 (released May 2023) removed support for language versions prior to 2.12 - meaning that you can no longer opt out of null-safety.