Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The new style JS annotation does not support class constructors as properties of other classes #54731

Closed
navaronbracke opened this issue Jan 25, 2024 · 2 comments
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-js-interop Issues that impact all js interop

Comments

@navaronbracke
Copy link

With the new @JS() annotation that is part of the new approach for JS interop (using dart:js_interop and friends),
there is yet no way to "namespace" the names in the annotation.

In the old, dart:html-era @JS() annotation, this was possible, because it handled dots in the name differently.

Use Case

Developers wanting to integrate JS libraries that define the following pattern:

class Foo {
  constructor(){}
}

// "MyLibrary" is a namespace do to speak
globalThis.MyLibrary = {
  Foo: Foo, // a function type, pointing to the constructor of "Foo"
};

In this case, defining the following static interop definition does not work:

import 'dart:js_interop';

@JS('MyLibrary.Foo')
@staticInterop
class Foo {
  external factory Foo({
    // args
  });
}

Getting access to the MyLibrary part of the namespace is possible, by using a static interop definition on the Window.

But leveraging the static interop pattern to go further down in the namespace does not work,
as this is interpreted as globalThis["MyLibrary.Foo"] instead of globalThis["MyLibrary"].Foo, as mentioned in #54531 (comment)

Thanks to #54534 being resolved, we can work around this manually by invoking callConstructor(), but this is both error prone, and nullifies the benefits of the static typing mechanism of the JS interop system. Furthermore, adding the static interop type to the invocation, i.e. callConstructor<Foo>() does not seem to work?

I have also tried adding

@JS('MyLibrary')
library;

at the top of the library unit, but to no avail.

Proposal

Support handling namespace-like constructs like in the use case above, by extending the existing interop tooling to support namespaces like in the example above. Maybe we could add a property to the JS annotation, to let developers indicate that it is a namespace subdivision, and not a dot in the name?

It is certainly possible that this is already somewhat possible today, but then I am unaware of the ways to do so.
In that case, documenting how to do it would be sufficient.

Dart 3.4.0-70.0.dev (dev) (Wed Jan 24 20:02:37 2024 -0800) on "macos_x64"
MacOS Sonoma 14.1.2 (Build 23B92)

@srujzs
Copy link
Contributor

srujzs commented Jan 25, 2024

Hmm, this works for me so I'm confused on where things are going wrong.

import 'dart:js_interop';

@JS()
external void eval(String code);

@JS('MyLibrary.Foo')
@staticInterop
class Foo {
  external factory Foo();
}

extension on Foo {
  external String get getter;
}

void main() {
  eval('''
    globalThis.MyLibrary = {};
    globalThis.MyLibrary.Foo = function Foo() {
      this.getter = 'hello world';
    }
  ''');
  print(Foo().getter);
}

prints "hello world", which is what we expect. We also have tests to ensure this e.g. https://github.com/dart-lang/sdk/blob/main/tests/lib/js/static_interop_test/external_static_member_lowerings_with_namespaces_test.dart. You can also namespace the library like that test shows.

Dart version: Dart SDK version: 3.4.0-edge.2f1c5c40555ac865159bf2a1c06ef303fdf334f0 (main) (Wed Jan 24 20:30:53 2024 -0800) on "macos_x64"

What error are you coming across?

@srujzs srujzs added area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-js-interop Issues that impact all js interop labels Jan 25, 2024
@navaronbracke
Copy link
Author

This turned out to be a user error on my end. I had forgotten to pass a thisArg to a callAsFunction invocation.

This is WAI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-web Use area-web for Dart web related issues, including the DDC and dart2js compilers and JS interop. web-js-interop Issues that impact all js interop
Projects
None yet
Development

No branches or pull requests

2 participants