mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 08:20:31 +00:00
[pkg:js] Update CHANGELOG, README, and WORKAROUNDS
Updates documentation now that static interop features are available. This should be the last component before the new `package:js` version is published. Change-Id: I9ddb494a5723036ca699bdcf10a000f7670cdfd8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/224401 Reviewed-by: Riley Porter <rileyporter@google.com> Commit-Queue: Srujan Gaddam <srujzs@google.com>
This commit is contained in:
parent
a9b33cfcd2
commit
7701bfb3e9
3 changed files with 156 additions and 14 deletions
|
@ -1,3 +1,7 @@
|
|||
## 0.6.4
|
||||
|
||||
* Includes `@staticInterop` to allow interop with native types from `dart:html`.
|
||||
|
||||
## 0.6.3
|
||||
|
||||
* Stable release for null safety.
|
||||
|
|
|
@ -131,6 +131,48 @@ void main() {
|
|||
}
|
||||
```
|
||||
|
||||
## Interop with native types using `@staticInterop`
|
||||
|
||||
Previously, you could not use `@JS()` or `@anonymous` types to interface with
|
||||
native types that were reserved within `dart:html` e.g. `Window`.
|
||||
|
||||
Using `@staticInterop` will now let you do so. However, it requires that there
|
||||
be no instance members within the class (constructors are still allowed). You
|
||||
can use static extension methods instead to declare these members. For example:
|
||||
|
||||
```dart
|
||||
@JS()
|
||||
library static_interop;
|
||||
|
||||
import 'dart:html' as html;
|
||||
|
||||
import 'package:js/js.dart';
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class JSWindow {}
|
||||
|
||||
extension JSWindowExtension on JSWindow {
|
||||
external String get name;
|
||||
String get nameAllCaps => name.toUpperCase();
|
||||
}
|
||||
|
||||
void main() {
|
||||
var jsWindow = html.window as JSWindow;
|
||||
print(jsWindow.name.toUpperCase() == jsWindow.nameAllCaps);
|
||||
}
|
||||
```
|
||||
|
||||
Note that in the above you can have both `external` and non-`external` members
|
||||
in the extension. You can have `external` variables, getters/setters, and
|
||||
methods within a static extension currently. These `external` members are
|
||||
lowered to their respective `js_util` calls under the hood. For example, the
|
||||
`external` `name` getter is equivalent to `js_util.getProperty(this, 'name')`.
|
||||
|
||||
In general, it's advised to use `@staticInterop` wherever you can over using
|
||||
just `@JS()`. There will be fewer surprises and it's aligned with the statically
|
||||
typed future planned for JS interop.
|
||||
|
||||
## Reporting issues
|
||||
|
||||
Please file bugs and feature requests on the [SDK issue tracker][issues].
|
||||
|
|
|
@ -19,13 +19,45 @@ platform libraries.
|
|||
As mentioned above, there exists stale interfaces. While some of these may be
|
||||
fixed in the source code, many might not.
|
||||
|
||||
In order to circumvent this, you can use the `js_util` library, like
|
||||
`getProperty`, `setProperty`, `callMethod`, and `callConstructor`.
|
||||
In order to work around this, you can use the annotation `@staticInterop` from
|
||||
`package:js`.
|
||||
|
||||
Let’s look at an example. `FileReader` is a `dart:html` interface that is
|
||||
missing the API `readAsBinaryString` ([#42834][]). We can work around this by
|
||||
doing something like the following:
|
||||
|
||||
```dart
|
||||
@JS()
|
||||
library workarounds;
|
||||
|
||||
import 'dart:html';
|
||||
|
||||
import 'package:async_helper/async_minitest.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:js/js.dart';
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class JSFileReader {}
|
||||
|
||||
extension JSFileReaderExtension on JSFileReader {
|
||||
external void readAsBinaryString(Blob blob);
|
||||
}
|
||||
|
||||
void main() async {
|
||||
var reader = new FileReader();
|
||||
reader.onLoad.listen(expectAsync((event) {
|
||||
String result = reader.result as String;
|
||||
Expect.equals(result, '00000000');
|
||||
}));
|
||||
var jsReader = reader as JSFileReader;
|
||||
jsReader.readAsBinaryString(new Blob(['00000000']));
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, you can directly use the `js_util` library, using the methods
|
||||
`getProperty`, `setProperty`, `callMethod`, and `callConstructor`.
|
||||
|
||||
```dart
|
||||
import 'dart:html';
|
||||
import 'dart:js_util' as js_util;
|
||||
|
@ -46,9 +78,37 @@ void main() async {
|
|||
}
|
||||
```
|
||||
|
||||
In the case where the API is missing a constructor, we can use
|
||||
`callConstructor`. For example, instead of using the factory constructor for
|
||||
`KeyboardEvent`, we can do the following:
|
||||
In the case where the API is missing a constructor, we can define a constructor
|
||||
within a `@staticInterop` class. Note that constructors, `external` or
|
||||
otherwise, are disallowed in extensions currently. For example:
|
||||
|
||||
```dart
|
||||
@JS()
|
||||
library workarounds;
|
||||
|
||||
import 'dart:js_util' as js_util;
|
||||
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:js/js.dart';
|
||||
|
||||
@JS('KeyboardEvent')
|
||||
@staticInterop
|
||||
class JSKeyboardEvent {
|
||||
external JSKeyboardEvent(String typeArg, Object keyboardEventInit);
|
||||
}
|
||||
|
||||
extension JSKeyboardEventExtension on JSKeyboardEvent {
|
||||
external String get key;
|
||||
}
|
||||
|
||||
void main() {
|
||||
var event = JSKeyboardEvent('KeyboardEvent',
|
||||
js_util.jsify({'key': 'A'}));
|
||||
Expect.equals(event.key, 'A');
|
||||
}
|
||||
```
|
||||
|
||||
or with `js_util`'s `callConstructor`:
|
||||
|
||||
```dart
|
||||
import 'dart:html';
|
||||
|
@ -73,11 +133,45 @@ There are several native interfaces that are suppressed e.g.
|
|||
`USBDevice` ([#42200][]) due to historical reasons. These native interfaces are
|
||||
marked with `@Native`, are private, and have no attributes associated with them.
|
||||
Therefore, unlike other `@Native` objects, we can’t access any of the APIs or
|
||||
attributes associated with this interface. We can use the `js_util` library
|
||||
again to circumvent this issue. For example, we can manipulate a
|
||||
`_SubtleCrypto` object:
|
||||
attributes associated with this interface. We can again either use the
|
||||
`@staticInterop` annotation or use the `js_util` library to circumvent this
|
||||
issue. For example, we can abstract a `_SubtleCrypto` object:
|
||||
|
||||
```dart
|
||||
@JS()
|
||||
library workarounds;
|
||||
|
||||
import 'dart:html';
|
||||
import 'dart:js_util' as js_util;
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:js/js.dart';
|
||||
|
||||
@JS()
|
||||
external Crypto get crypto;
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class JSSubtleCrypto {}
|
||||
|
||||
extension JSSubtleCryptoExtension on JSSubtleCrypto {
|
||||
external dynamic digest(String algorithm, Uint8List data);
|
||||
Future<ByteBuffer> digestFuture(String algorithm, Uint8List data) =>
|
||||
js_util.promiseToFuture(digest(algorithm, data));
|
||||
}
|
||||
|
||||
void main() async {
|
||||
var subtle = crypto.subtle! as JSSubtleCrypto;
|
||||
var digest = await subtle.digestFuture('SHA-256', Uint8List(16));
|
||||
}
|
||||
```
|
||||
|
||||
or with `js_util`:
|
||||
|
||||
```dart
|
||||
@JS()
|
||||
library workarounds;
|
||||
|
||||
import 'dart:html';
|
||||
import 'dart:js_util' as js_util;
|
||||
import 'dart:typed_data';
|
||||
|
@ -96,10 +190,13 @@ void main() async {
|
|||
}
|
||||
```
|
||||
|
||||
What you shouldn’t do is attempt to cast these native objects using your own JS
|
||||
interop types, e.g.
|
||||
What you shouldn’t do is attempt to cast these native objects using the
|
||||
non-`@staticInterop` `package:js` types e.g.
|
||||
|
||||
```dart
|
||||
@JS()
|
||||
library workarounds;
|
||||
|
||||
import 'dart:html';
|
||||
|
||||
import 'package:js/js.dart';
|
||||
|
@ -115,14 +212,13 @@ void main() {
|
|||
}
|
||||
```
|
||||
|
||||
With the above, you’ll see a type error:
|
||||
With the above, you’ll see a static error:
|
||||
|
||||
`Uncaught TypeError: Instance of 'SubtleCrypto': type 'Interceptor' is not a subtype of type 'SubtleCrypto'`
|
||||
`Error: Non-static JS interop class 'SubtleCrypto' conflicts with natively supported class '_SubtleCrypto' in 'dart:html'.`
|
||||
|
||||
This is because the types in the `@Native` annotation are reserved and the above
|
||||
leads to namespace conflicts between the `@Native` type and the user JS interop
|
||||
type in the compiler. These `@Native` types inherit the `Interceptor` class,
|
||||
which is why you see the message above.
|
||||
type in the compiler. `@staticInterop` classes, however, don't have this issue.
|
||||
|
||||
[#42834]: https://github.com/dart-lang/sdk/issues/42834
|
||||
[#42200]: https://github.com/dart-lang/sdk/issues/42200
|
||||
|
|
Loading…
Reference in a new issue