Dart JS Interop: Why 'Never' Isn't Allowed

by Mei Lin 43 views

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