Skip to content

Proposal: Add Support for JS Constructor Interoperability #222

Closed
@nikeokoronkwo

Description

@nikeokoronkwo

Synopsis

This proposal aims to suggest the possible addition of support for Dart-JavaScript Interoperability with JavaScript constructor methods and other related functionality.

Dart-JavaScript interop works for a good majority of use cases, such as for interoping with classes (via extension types) and functions. However, one feature of JavaScript that Dart doesn't have support for is being able to interop and emulate javascript constructors.

Overview

In JavaScript, if you called ArrayBuffer, it would return a function, representing the constructor function. If you called ArrayBuffer() however (I'm aware there should be a new keyword infront), it would return an object

typeof ArrayBuffer; // "function"
typeof new ArrayBuffer(); // "object"

As of now, if we wanted to perform interop with the javascript ArrayBuffer class, we would have to pick between one of these: either to interop with the class object as an extension type, or to interop with the constructor as a function call

import 'dart:js_interop';
// Either
@JS("ArrayBuffer")
external JSFunction get ArrayBuffer;

// Or
extension type ArrayBuffer._(JSObject _) implements JSObject {
  // methods and fields...
}

If we wanted to incorporate both, we would need to give separate names.

TThis proposal suggests incorporating an extension type into a single interface, where the object's constructor can be represented through an API call in the extension type. Although it would be preferable to simply use the object's name, an alternative solution could be considered.

extension type ArrayBuffer._(JSObject _) implements JSObject {
  // methods and fields...
}

void main() {
  print(ArrayBuffer); // prints the constructor
  print(ArrayBuffer()); // prints object
}

I would be glad to enlighten more, but in order to not make this proposal too lengthy as it already is, I would respond to any questions concerning this in the comments (if any).

Importance

One of the main reasons behind this proposal has been for the adoption of Web Components (which will be discussed in more detail in a later proposal). I tried working on making Web Components on my own using current Dart interop, but it wasn't successful in any direction.

// imports defined

extension type MyCustomElement._(JSObject _) implements HTMLElement {
  // EDIT: Forgot you can't call `super` in an extension type 
  // Another issue facing the implementation of Web Components with the next-gen JS Interop
}

void main() {
  // ?? - No constructor possible
  window.customElements.define("my-element", /* ?? */);
}

The best case I had was the fact that I wasn't able to use the window.customElements.define method because I couldn't obtain the constructor of the new class. I also couldn't call MyCustomElement(), as HTMLElement, as well as any objects that extend it, do not allow calls to their constructors directly.

It also would not be possible to obtain the constructor for a class/extension type A made in Dart that extends/implements B that is in Dart but exported from JavaScript, since A isn't defined in JavaScript.

/// `B` represents the Dart API for the JavaScript `C` object
@JS("C")
extension type B._(JSObject _) implements JSObject {
  external B();
  // more code...
}

// If we wanted the constructor for JavaScript's `C` object, we would need to do something like this
@JS("C")
external JSFunction get cConstructor;

// `A` is now an extension type of underlying type `B` and implements `B`
extension type A._(B _) implements B {
  A(): _ = B();
  // more code...
}

void main() {
  print(cConstructor); // Constructor of `C`
  print(B()); // Object of `C`
  print(A()); // Object of `A`
  // Cannot get constructor of A

Additional Information/Closing

If the Dart team would be interested, I would be glad to join in helping to make this possible/contributing for this proposal.
Web Components is another issue of it's own I guess, so I'd want to start with this one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions