Dart JS Interop Function overloading for typing

Issue

I have defined a D3 Dart JS Interop, that I am using to set values on D3 objects and call them, which is working well. Unfortunately D3 uses the same function for both setters and getters, and depends on the amount of arguments to set them apart.

For example, the Arc object has the ability to set and get an InnerRadius like this (in JavaScript):

var arc = d3.arc().innerRadius(25);
//arc with innerRadius of 25
var radius = arc.innerRadius();
//radius is 25

The Dart wrapper I’ve defined looks like this:

@JS('d3')
library d3;

import 'dart:js';
import "package:js/js.dart";

@JS("arc")
class Arc {
  external Arc innerRadius(num innerRadius);
  external Arc();
}

And I would like to define a wrapper like this:

@JS("arc")
class Arc {
  external Arc innerRadius(num innerRadius);
  external num innerRadius();
  external Arc();
}

But it complains about the name already being defined. How do I define the dart wrapper so that I can both set and recall the value while also typing it?

D3 defines an Arc like this:

function arcInnerRadius(d) {
    return d.innerRadius;
}

function constant$1(x) {
    return function constant() {
        return x;
    }
}

function arc() {
    var innerRadius = arcInnerRadius;

    function arc() {
        var r0 = +innerRadius.apply(this, arguments);
        //calculate and return path
    }

    arc.innerRadius = function(_) {
        if(arguments.length) {
            if(_ === "function") {
                innerRadius = _;
            } else {
                innerRadius = constant$1(+_);
            }
            return arc;
        }
        return innerRadius;
    }

    return arc;
}

Thus the getter and setter syntax doesn’t wrap it properly. As setting innerRadius sets the function which is not called by the code, not the internal value which is.

There is an ugly solution, wherein if you drop the typing and make the parameter optional it allows you to do both. So my current solution is to define the wrapper like this:

@JS('d3')
library d3;

import 'dart:js';
import "package:js/js.dart";


@JS("arc")
class Arc {
    external Arc();
    external innerRadius([num innerRadius]);
}

Which allows you to call it like this:

Arc arc = new Arc()
    ..innerRadius(25);
num radius = arc.innerRadius();

This works but has the downside in that we lose some of the value of typing in that I can’t type force the radius to be a number. That is, this is equally valid:

Point<num> radius = arc.innerRadius();

And it won’t explode until run-time.

So this matches the function it is mapping and it works, but what I’m really after is the ability to do it with types and thus define a function with the same name that can both return the same type.

Solution

We no longer need to define our own JavaScript definition files, as Dart has a tool for doing so from TypeScript files. The JS Facade Generator.
There’s more information on using this on the official Dart Page about JS Interoperability.

Thanks to the DefinitelyTyped project, there’s almost certainly a TypeScript file out there that will allow you to do this. If not, write one of those instead, it’ll help both the Dart and TypeScript communities.

Answered By – Digital Deception

Answer Checked By – Dawn Plyler (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.