Skip to main content

Dart type you have NEVER used

· 4 min read

Dart type system goes: never say never…

Header image

First of all, the title of this article could seem like just another clickbait but let me explain. Sure, there is a chance that you have used this type, but for the sake of a pun (and I REALLY enjoy them), I had to do it - oh well, here we are now. The thing is, the type I am going to talk about in this article is actually called Never.

note

All the information provided in this article is based on the official Dart language documentation.

Dart type system before and after null safety

Dart type system before (left) and after (right) null safety

To understand the concept of the type Never, first, we must understand the difference between the Dart type system before and after null safety. That was never an easy task for me, but let’s try.

Before sound null-safety in Dart, Null was a subtype of all types (look at the graph on the left in the picture above). After introducing null-safety, the Null type is no longer a subtype of all types (the right graph in the same picture above), all types became non-nullable by default — and you could hear this everywhere when talking about sound null-safety of Dart.

However, by looking at this new Dart types’ structure, we cannot easily identify what’s the top-most and bottom-most type. Well, for the top-most type it is quite clear: we do not have a dedicated named type, but we can define it as a nullable Object (Object?) — a combination of Object and Null types. When you think about it, every nullable type is just a combination of any non-nullable type and Null. For instance, double + Null = double?, String + Null = String? and so on.

Ok, but how about the bottom-most type? For this, the Dart team went brrrr and just simply introduced a new named type called Never.

Dart type system with Never

This new bottom type has no values. However, you can use it as a type annotation. How, why? Let’s check it out👇🏻

When should I use Never in my code?

I do adore how this type is described in the documentation:

On the rare occasion that you need a bottom type, use Never instead of Null. If you don’t know if you need a bottom type, you probably don’t.

Ok, but there are cases when the type Never could be used in the code. Whether that’s useful or not, I will leave it for you to decide.

When the return type of an expression is Never, what does it mean? Well, you could say that it should return something of type Never, but it indicates that the expression can never successfully finish evaluating. Does it mean that the expression or function would get stuck at that point? No, just “it must throw an exception, abort, or otherwise ensure that the surrounding code expecting the result of the expression never runs.” This idea could be applied when creating helper functions for validation:

// User data class
class User {
final String email;
final String password;

const User({
required this.email,
required this.password,
});


String toString() => 'User {email: $email, password: $password}';
}

// Helper function for required field validation
Never isRequired(String property) {
throw ArgumentError('$property is required.');
}

void main() {
String? email;
String? password;

// ...
// Some code to get email and password
// ...

if (email == null || email.isEmpty) isRequired('email');
if (password == null || password.isEmpty) isRequired('password');

final user = User(email: email, password: password);

// ...
// Do something with the user data
// ...
}

This example compiles without any error!

Notice that email and password are required properties when creating the User object and there is no return or throw keywords in the validation code. The control flow analysis knows that the return type of isRequired() helper function is Never, hence in case of any if condition being true, the code must abort somehow. It allows using email and password as non-nullable variables later on.

In other words, using Never in your own APIs lets you extend Dart’s reachability analysis.

In my opinion, there is not much difference between throwing a custom exception and using a helper function of type Never. However, if you find such implementation convenient, feel free to use it in your code!


Save trees. Stay SOLID. Thanks for reading.

Don't miss my next article!

Subscribe to get the latest content by email.

    No spam. Unsubscribe at any time.