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
|
## 0.6.3
|
||||||
|
|
||||||
* Stable release for null safety.
|
* 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
|
## Reporting issues
|
||||||
|
|
||||||
Please file bugs and feature requests on the [SDK issue tracker][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
|
As mentioned above, there exists stale interfaces. While some of these may be
|
||||||
fixed in the source code, many might not.
|
fixed in the source code, many might not.
|
||||||
|
|
||||||
In order to circumvent this, you can use the `js_util` library, like
|
In order to work around this, you can use the annotation `@staticInterop` from
|
||||||
`getProperty`, `setProperty`, `callMethod`, and `callConstructor`.
|
`package:js`.
|
||||||
|
|
||||||
Let’s look at an example. `FileReader` is a `dart:html` interface that is
|
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
|
missing the API `readAsBinaryString` ([#42834][]). We can work around this by
|
||||||
doing something like the following:
|
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
|
```dart
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
import 'dart:js_util' as js_util;
|
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
|
In the case where the API is missing a constructor, we can define a constructor
|
||||||
`callConstructor`. For example, instead of using the factory constructor for
|
within a `@staticInterop` class. Note that constructors, `external` or
|
||||||
`KeyboardEvent`, we can do the following:
|
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
|
```dart
|
||||||
import 'dart:html';
|
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
|
`USBDevice` ([#42200][]) due to historical reasons. These native interfaces are
|
||||||
marked with `@Native`, are private, and have no attributes associated with them.
|
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
|
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
|
attributes associated with this interface. We can again either use the
|
||||||
again to circumvent this issue. For example, we can manipulate a
|
`@staticInterop` annotation or use the `js_util` library to circumvent this
|
||||||
`_SubtleCrypto` object:
|
issue. For example, we can abstract a `_SubtleCrypto` object:
|
||||||
|
|
||||||
```dart
|
```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:html';
|
||||||
import 'dart:js_util' as js_util;
|
import 'dart:js_util' as js_util;
|
||||||
import 'dart:typed_data';
|
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
|
What you shouldn’t do is attempt to cast these native objects using the
|
||||||
interop types, e.g.
|
non-`@staticInterop` `package:js` types e.g.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
|
@JS()
|
||||||
|
library workarounds;
|
||||||
|
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
|
|
||||||
import 'package:js/js.dart';
|
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
|
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
|
leads to namespace conflicts between the `@Native` type and the user JS interop
|
||||||
type in the compiler. These `@Native` types inherit the `Interceptor` class,
|
type in the compiler. `@staticInterop` classes, however, don't have this issue.
|
||||||
which is why you see the message above.
|
|
||||||
|
|
||||||
[#42834]: https://github.com/dart-lang/sdk/issues/42834
|
[#42834]: https://github.com/dart-lang/sdk/issues/42834
|
||||||
[#42200]: https://github.com/dart-lang/sdk/issues/42200
|
[#42200]: https://github.com/dart-lang/sdk/issues/42200
|
||||||
|
|
Loading…
Reference in a new issue