javascriptdartdart-js-interop

Getting an arbitrary property from a JavaScript object in Dart


Edit: Here is a minimal project that illustrates my issue. You can see the error described by serving it to the browser: pub get and then either pub serve (dartium) or pub build --mode=debug (other browsers).

How can I access an arbitrary JavaScript property from Dart through a JsObjectImpl? I am using the ace.js library with an interop to Dart that I've adapted from a typescript interface, and the method I am calling returns a plain javascript object with key-value pairs.

Dart gives me a JsObjectImpl, which cannot be casted to a Map or a JsObject, both of which have [] accessors. It confusingly seems to inherit from the deprecated JSObject (note the 's' is capitalized in the latter) which does not have the [] accessor, so I can't get the data out.

Some error messages:

  1. When attempting a cast from JsObjectImpl to JsObject:
    ORIGINAL EXCEPTION: type 'JSObjectImpl' is not a subtype of type 'JsObject' of 'obj' where JSObjectImpl is from dart:js JsObject is from dart:js. I get a similar message when using Map as well.

  2. Looking at the object in the debugger, I can frustratingly see the property in JS view but not in the Dart object:
    Chrome debugger showing a property with the key "4"
    The 4: Object is the data I want.


Solution

  • Ok, this was a fun one, happy holidays :)

    It looks like Map is not a supported auto-conversion for package:js. So a couple of things:

    1. Filed https://github.com/dart-lang/sdk/issues/28194
    2. Sent your a PR introducing a workaround

    For interested parties, we can use the browser-native Object.keys:

    @JS()
    library example;
    
    import 'package:js/js.dart';
    
    /// A workaround to converting an object from JS to a Dart Map.
    Map jsToMap(jsObject) {
      return new Map.fromIterable(
        _getKeysOfObject(jsObject),
        value: (key) => getProperty(jsObject, key),
      );
    }
    
    // Both of these interfaces exist to call `Object.keys` from Dart.
    //
    // But you don't use them directly. Just see `jsToMap`.
    @JS('Object.keys')
    external List<String> _getKeysOfObject(jsObject);
    

    And call it once we have an arbitrary JavaScript object:

    var properties = jsToMap(toy.getData());
    print(properties);