Open Zekfad opened 1 month ago
Summary: The dart:js_interop
library lacks a proper way to define getters and setters for Dart objects that are exposed to JavaScript. This makes it difficult to interact with JavaScript APIs that rely on these features, such as feature detection code.
Here is my current defineProperty
:
With that feature detection looks like this:
final supportsRequestStreams = (){
var duplexAccessed = false;
final options = RequestOptions(
method: 'POST',
body: RequestBody.fromReadableStream(ReadableStream()),
);
defineProperty(
options,
'duplex'.toJS.toJSStringOrJSSymbol,
JSAccessorDescriptor(
get: (context) {
duplexAccessed = true;
return RequestDuplex.half.value.toJS;
},
),
);
final hasContentType = Request('', options).headers.has('Content-Type');
return duplexAccessed && !hasContentType;
}();
I think toJSCaptureThis
is essential either way, but this is useful to see one more motivation.
I'm hesitant to say we should add Object.defineProperty
in dart:js_interop_unsafe
. Beyond the fact that it's an "unsafe" API, it necessitates a separate definition for the descriptor and I'm not sure it will be used often enough to warrant its own definition. There's been general brainstorming about moving such APIs to package:web
in a section that's specific to JS instead of the DOM - maybe that might be the place for this in the long-term.
Purely FYI - somewhat related to defining JS getters/setters is createJSInteropWrapper that wraps a Dart object in a JS wrapper with Dart getters and setters being called in JS getters and setters using Object.defineProperty
. I can't tell if that is useful here or not.
I believe Object.defineProperty
is also useful for creating immutable properties which can be useful for exposed objects.
Another solution for this exact issue is to add a new annotation, such that getters/setters defined on JS extension type would remain getters/setters in JS side. This will make it necessary to bind this, as "this" will point to JS value with JS interop type.
Currently JS interop missing a good way to define getters and setters that would be available from JS side.
We have no
Object.defineProperty
function built-in. Even if we had such, currently it's very hacky and requires some workarounds (see this comment https://github.com/dart-lang/sdk/issues/54381#issuecomment-2208745412 ), which wont always work.My actual use case is to implement feature detection code from following article: https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests#feature_detection
That is:
As you can see this requires using a getter to check that property is accessed. While exactly this use can can be achieved without doing much of beforementioned workaround, there are still some issues.
Using a setter would requires somehow externally passing reference to the object. Meaning there's no good way to do something like this (example from MDN)
Mostly this issue is not about exposing Dart API as JS interface, but rather passing data to existing JS APIs.
I think to fix this:
Object.defineProperty
with idiomatic API indart:js_interop_unsafe
.