Dart: The Features I Wish It Had

I recently spent a few months working on a Flutter project. If you aren’t familiar with Flutter, it’s a cross-platform user interface toolkit that uses the Dart programming language.

High-level summary of Dart from Bob Nystrom at Google I/O 2019. Source

Dart is object-oriented, but it provides a lot of features from functional programming languages that make it nicer to work with compared to other OOP languages. With that being said, I think a few key features are missing from Dart that I think would make the language much nicer to use.

Feature: Null Safety

With the introduction of sound null safety in Dart 2.12, all values are non-nullable by default unless otherwise stated. A lot of the popular packages on pub.dev have already migrated to being null safe.

Unfortunately, this migration will take time. If you happen to be using one of the packages that has not migrated yet, you won’t be able to use null safety in your own app. If this happens, you will need to use the --no-sound-null-safety flag when running, building, and testing.

The Dart language already has this feature, and it is very nice in the scenarios where it applies. However, the community needs some time to finish migrating their packages for this to be fully supported.

Feature: Type Narrowing

One of the main features I enjoy in TypeScript is the ability for the compiler to perform narrowing of types.

Lets take a look at the following example:


type Person = {
  firstName: String;
  lastName?: String;
};

const bob: Person = {
  firstName: "Bob",
  lastName: "Smith",
};

function sayHello(firstName: String, lastName: String) {
  console.log(`Hello, ${firstName} ${lastName}`);
}

// bob.lastName is String | undefined here
sayHello(bob.firstName, bob.lastName); // Compile error!!

if (bob.lastName) {
  // bob.lastName is just String here
  sayHello(bob.firstName, bob.lastName);
}

As with most code examples in blog posts, this example is contrived, but it highlights how narrowing works.

Since the lastName property is either a String or undefined, we can’t use it in the sayHello function. That’s because it’s expecting two values of type String. By checking the truthy value of bob.lastName in the if statement, the compiler knows (within the block of that if statement) the type of bob.lastName can’t be undefined (since undefined is falsy in JavaScript). The type is narrowed to a String automatically.

Unfortunately, the Dart compiler does not support type narrowing at this time. To use the nullable String in a comparable Dart example, you would have to cast the value to a non-nullable String before passing it into the function. The other option is to use the null assertion operator!” to assert that the value won’t be null at run-time.

Feature: Algebraic Data Types

Having previously worked with TypeScript on several projects, I have grown accustomed to creating larger types by AND-ing and OR-ing smaller types together.

Attempting to do this in Dart would result in creating yet another class and would often require some combination of polymorphism, inheritance, generics, salt, pepper, and olive oil. There does appear to be at least one package on pub.dev called adt that attempts to auto-generate a lot of the extra boilerplate for you.

I’ve experienced the power of algebraic data types in other languages (mainly TypeScript and F#) for domain modeling. So, it’s hard for me to work with a language that doesn’t offer these features out of the box.

The Future of Dart Features

The Dart team has made some incredible changes to Dart (the language and the platform) in recent years. It has transitioned from a dynamically typed language that primarily targeted JavaScript to a strongly typed, null-safe language targeting multiple CPU architectures.

That’s an incredible transition in a short amount of time, and I can’t wait to see where Dart ends up in another few years. I’m just hoping it has some of the features on my wishlist by then.