Dart JS Interop: Why 'Never' Isn't Allowed
Introduction
Hey guys! Today, let's dive deep into a fascinating and somewhat quirky corner of Dart's JavaScript interop capabilities. Specifically, we're going to chat about the Never
type and why it's currently causing a bit of a headache when you're trying to bridge Dart and JavaScript code. If you've ever scratched your head at cryptic error messages when trying to use toJS
with functions that never return, you're in the right place. We'll break down the issue, explore workarounds, and discuss why this limitation exists in the first place. So, grab your favorite beverage, and let's get started!
Understanding the Never
Type in Dart
First things first, let's make sure we're all on the same page about what the Never
type actually means in Dart. In Dart, Never
is a special bottom type that signifies a function that never successfully completes. This might sound a bit strange at first, but it's incredibly useful for a couple of key scenarios. Think about functions that always throw an exception or functions that enter an infinite loop – these functions, by definition, never return a value. That’s where Never
comes in handy. Using Never
as a return type provides valuable information to the Dart compiler and to other developers reading your code. It clearly communicates that this function is not intended to return normally.
For example, consider a function like this:
Never throwError(String message) {
throw Exception(message);
}
Here, throwError
is explicitly declared to return Never
. This tells anyone using this function (including the Dart analyzer) that calling throwError
will always result in an exception being thrown, and no further code in the same execution path will be reached. This can be super useful for catching potential bugs early and making your code more robust. Another common scenario is when dealing with infinite loops:
Never infiniteLoop() {
while (true) {
// Do something forever...
}
}
Again, infiniteLoop
is declared to return Never
because it’s designed to run indefinitely. This helps the Dart compiler make assumptions about code flow and can lead to optimizations. Now, you might be wondering, “Why is this causing problems with JavaScript interop?” That’s the million-dollar question, and we're about to unravel it.
The Problem with Never
and toJS
Okay, so we know what Never
is. Now, let's talk about the specific issue at hand. As the original post highlights, there's a snag when you try to use functions that return Never
with Dart's toJS
functionality. If you're not familiar, toJS
is a crucial part of Dart's interop story, allowing you to seamlessly pass Dart functions to JavaScript code. This is incredibly powerful for building web applications where you might need to leverage existing JavaScript libraries or frameworks.
The problem arises when you try to convert a Dart function that returns Never
into a JavaScript function using toJS
. Dart throws an error, specifically: “Function converted via ‘toJS’ contains invalid types in its function signature: 'Never Function().” This is a bummer because it limits our ability to accurately represent JavaScript functions that, from a logical perspective, should never return.
Let's revisit the example from the original post:
import 'dart:js_interop';
void main() {
(() => throw "oh no").toJS;
}
This seemingly simple code snippet fails. Why? Because the anonymous function `() => throw