blob: 2b9e1034a0a3479202204aa8f77b46474c8724e1 [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// A library for JS interop. Includes a JS type hierarchy to facilitate sound
/// interop with JS. The JS type hierarchy is modeled after the actual type
/// hierarchy in JS, and not the Dart type hierarchy.
///
/// Note: The exact semantics of JS types currently depend on their backend
/// implementation. JS backends currently support conflating Dart types and JS
/// types, whereas Wasm backends do not. Over time we will try and unify backend
/// semantics as much as possible.
///
/// **WARNING**:
/// The following types will be sealed in the near future. Do *not* subtype
/// the types in this library as the subtypes will be broken.
///
/// {@category Web}
library dart.js_interop;
import 'dart:_js_types' as js_types;
import 'dart:typed_data';
/// Export the `dart:_js_annotations` version of the `@JS` annotation. This is
/// mostly identical to the `package:js` version, except this is meant to be
/// used for sound top-level external members and inline classes instead of the
/// `package:js` classes. Also allow use of `@staticInterop` classes with JS
/// types as well as export functionality.
export 'dart:_js_annotations' show JS, staticInterop, anonymous, JSExport;
/// The annotation for object literal constructors.
///
/// Use this on an external constructor for an interop inline class that only
/// has named args in order to create object literals performantly. The
/// resulting object literal will use the parameter names as keys and the
/// provided arguments as the values.
///
/// Note that object literal constructors ignore the default values of
/// parameters and only include the arguments you provide in the invocation of
/// the constructor. This is similar to the `@anonymous` annotation in
/// `package:js`.
class ObjectLiteral {
const ObjectLiteral();
}
/// The overall top type in the JS types hierarchy.
typedef JSAny = js_types.JSAny;
/// The representation type of all JavaScript objects for inline classes,
/// [JSObject] <: [JSAny].
///
/// This is the supertype of all JS objects, but not other JS types, like
/// primitives. This is the only allowed `on` type for inline classes written by
/// users to model JS interop objects. See https://dart.dev/web/js-interop for
/// more details on how to use JS interop.
// TODO(srujzs): This class _must_ be sealed before we can make this library
// public. Either use the CFE mechanisms that exist today, or use the Dart 3
// sealed classes feature.
// TODO(joshualitt): Do we need to seal any other JS types on JS backends? We
// probably want to seal all JS types on Wasm backends.
typedef JSObject = js_types.JSObject;
/// The type of all JS functions, [JSFunction] <: [JSObject].
typedef JSFunction = js_types.JSFunction;
/// The type of all Dart functions adapted to be callable from JS. We only allow
/// a subset of Dart functions to be callable from JS, [JSExportedDartFunction]
/// <: [JSFunction].
// TODO(joshualitt): Detail exactly what are the requirements.
typedef JSExportedDartFunction = js_types.JSExportedDartFunction;
/// The type of JS promises and promise-like objects, [JSPromise] <: [JSObject].
typedef JSPromise = js_types.JSPromise;
/// The type of all JS arrays, [JSArray] <: [JSObject].
typedef JSArray = js_types.JSArray;
/// The type of all Dart objects exported to JS. If a Dart type is not
/// explicitly marked with `@JSExport`, then no guarantees are made about its
/// representation in JS. [JSExportedDartObject] <: [JSObject].
typedef JSExportedDartObject = js_types.JSExportedDartObject;
/// The type of JS array buffers, [JSArrayBuffer] <: [JSObject].
typedef JSArrayBuffer = js_types.JSArrayBuffer;
/// The type of JS byte data, [JSDataView] <: [JSObject].
typedef JSDataView = js_types.JSDataView;
/// The abstract supertype of all JS typed arrays, [JSTypedArray] <: [JSObject].
typedef JSTypedArray = js_types.JSTypedArray;
/// The typed arrays themselves, `*Array` <: [JSTypedArray].
typedef JSInt8Array = js_types.JSInt8Array;
typedef JSUint8Array = js_types.JSUint8Array;
typedef JSUint8ClampedArray = js_types.JSUint8ClampedArray;
typedef JSInt16Array = js_types.JSInt16Array;
typedef JSUint16Array = js_types.JSUint16Array;
typedef JSInt32Array = js_types.JSInt32Array;
typedef JSUint32Array = js_types.JSUint32Array;
typedef JSFloat32Array = js_types.JSFloat32Array;
typedef JSFloat64Array = js_types.JSFloat64Array;
// The various JS primitive types. Crucially, unlike the Dart type hierarchy,
// none of these are subtypes of [JSObject], but rather they are logically
// subtypes of [JSAny].
/// The type of JS numbers, [JSNumber] <: [JSAny].
typedef JSNumber = js_types.JSNumber;
/// The type of JS booleans, [JSBoolean] <: [JSAny].
typedef JSBoolean = js_types.JSBoolean;
/// The type of JS strings, [JSString] <: [JSAny].
typedef JSString = js_types.JSString;
/// `JSUndefined` and `JSNull` are actual reified types on some backends, but
/// not others. Instead, users should use nullable types for any type that could
/// contain `JSUndefined` or `JSNull`. However, instead of trying to determine
/// the nullability of a JS type in Dart, i.e. using `?`, `!`, `!= null` or `==
/// null`, users should use the provided helpers below to determine if it is
/// safe to downcast a potentially `JSNullable` or `JSUndefineable` object to a
/// defined and non-null JS type.
// TODO(joshualitt): Investigate whether or not it will be possible to reify
// `JSUndefined` and `JSNull` on all backends.
extension NullableUndefineableJSAnyExtension on JSAny? {
external bool get isUndefined;
external bool get isNull;
bool get isUndefinedOrNull => isUndefined || isNull;
bool get isDefinedAndNotNull => !isUndefinedOrNull;
}
/// The type of `JSUndefined` when returned from functions. Unlike pure JS,
/// no actual object will be returned.
typedef JSVoid = js_types.JSVoid;
// Extension members to support conversions between Dart types and JS types.
// Not all Dart types can be converted to JS types and vice versa.
// TODO(joshualitt): We might want to investigate using inline classes instead
// of extension methods for these methods.
/// [JSExportedDartFunction] <-> [Function]
extension JSExportedDartFunctionToFunction on JSExportedDartFunction {
external Function get toDart;
}
extension FunctionToJSExportedDartFunction on Function {
external JSExportedDartFunction get toJS;
}
/// [JSExportedDartObject] <-> [Object]
extension JSExportedDartObjectToObject on JSExportedDartObject {
external Object get toDart;
}
extension ObjectToJSExportedDartObject on Object {
external JSExportedDartObject get toJS;
}
/// [JSPromise] -> [Future<JSAny?>].
extension JSPromiseToFuture on JSPromise {
external Future<JSAny?> get toDart;
}
// TODO(joshualitt): On Wasm backends List / Array conversion methods will
// copy, and on JS backends they will not. We should find a path towards
// consistent semantics.
/// [JSArrayBuffer] <-> [ByteBuffer]
extension JSArrayBufferToByteBuffer on JSArrayBuffer {
external ByteBuffer get toDart;
}
extension ByteBufferToJSArrayBuffer on ByteBuffer {
external JSArrayBuffer get toJS;
}
/// [JSDataView] <-> [ByteData]
extension JSDataViewToByteData on JSDataView {
external ByteData get toDart;
}
extension ByteDataToJSDataView on ByteData {
external JSDataView get toJS;
}
/// [JSInt8Array] <-> [Int8List]
extension JSInt8ArrayToInt8List on JSInt8Array {
external Int8List get toDart;
}
extension Int8ListToJSInt8Array on Int8List {
external JSInt8Array get toJS;
}
/// [JSUint8Array] <-> [Uint8List]
extension JSUint8ArrayToUint8List on JSUint8Array {
external Uint8List get toDart;
}
extension Uint8ListToJSUint8Array on Uint8List {
external JSUint8Array get toJS;
}
/// [JSUint8ClampedArray] <-> [Uint8ClampedList]
extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray {
external Uint8ClampedList get toDart;
}
extension Uint8ClampedListToJSUint8ClampedArray on Uint8ClampedList {
external JSUint8ClampedArray get toJS;
}
/// [JSInt16Array] <-> [Int16List]
extension JSInt16ArrayToInt16List on JSInt16Array {
external Int16List get toDart;
}
extension Int16ListToJSInt16Array on Int16List {
external JSInt16Array get toJS;
}
/// [JSUint16Array] <-> [Uint16List]
extension JSUint16ArrayToInt16List on JSUint16Array {
external Uint16List get toDart;
}
extension Uint16ListToJSInt16Array on Uint16List {
external JSUint16Array get toJS;
}
/// [JSInt32Array] <-> [Int32List]
extension JSInt32ArrayToInt32List on JSInt32Array {
external Int32List get toDart;
}
extension Int32ListToJSInt32Array on Int32List {
external JSInt32Array get toJS;
}
/// [JSUint32Array] <-> [Uint32List]
extension JSUint32ArrayToUint32List on JSUint32Array {
external Uint32List get toDart;
}
extension Uint32ListToJSUint32Array on Uint32List {
external JSUint32Array get toJS;
}
/// [JSFloat32Array] <-> [Float32List]
extension JSFloat32ArrayToFloat32List on JSFloat32Array {
external Float32List get toDart;
}
extension Float32ListToJSFloat32Array on Float32List {
external JSFloat32Array get toJS;
}
/// [JSFloat64Array] <-> [Float64List]
extension JSFloat64ArrayToFloat64List on JSFloat64Array {
external Float64List get toDart;
}
extension Float64ListToJSFloat64Array on Float64List {
external JSFloat64Array get toJS;
}
/// [JSArray] <-> [List]
extension JSArrayToList on JSArray {
external List<JSAny?> get toDart;
}
extension ListToJSArray on List<JSAny?> {
external JSArray get toJS;
}
/// [JSNumber] <-> [double].
extension JSNumberToDouble on JSNumber {
external double get toDart;
}
extension DoubleToJSNumber on double {
external JSNumber get toJS;
}
/// [num] -> [JSNumber].
extension NumToJSExtension on num {
JSNumber get toJS => DoubleToJSNumber(toDouble()).toJS;
}
/// [JSBoolean] <-> [bool]
extension JSBooleanToBool on JSBoolean {
external bool get toDart;
}
extension BoolToJSBoolean on bool {
external JSBoolean get toJS;
}
/// [JSString] <-> [String]
extension JSStringToString on JSString {
external String get toDart;
}
extension StringToJSString on String {
external JSString get toJS;
}