// 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. /// Interoperability, "interop" for short, with JavaScript and browser APIs. /// /// JavaScript interop allows a Dart program to interact with a JavaScript /// runtime. This can, for example, be to access JavaScript declarations and /// interact with JavaScript values, or to adapt Dart values so that they can be /// passed to and used by JavaScript code. /// /// This JavaScript interop library works by introducing an abstraction over /// JavaScript values, a Dart type hierarchy ("JS types") which mirrors known /// JavaScript types, and a framework for introducing new Dart types that bind /// Dart type declarations to JavaScript values and external member declarations /// to JavaScript APIs. /// /// This abstraction allows the same interop API to be used both when the Dart /// code is compiled to JavaScript and when compiled to Wasm. /// /// See https://dart.dev/interop/js-interop for more details on usage, types, /// and previous JavaScript interop. /// /// > [!NOTE] /// > The types defined in this library only provide static guarantees. /// > The runtime types differ based on the backend, so it is important to rely /// > on static functionality like the conversion functions, for example `toJS` /// > and not runtime mechanisms like type checks (`is`) and casts (`as`). /// > Similarly, `identical` may return different results for the same JS value /// > depending on the compiler. Use `==` to check for equality of two JS types /// > instead. /// /// {@category Web} library; import 'dart:_internal' show Since; import 'dart:_js_annotations' show JSExport; import 'dart:_js_types'; import 'dart:js_interop_unsafe'; import 'dart:typed_data'; // To support an easier transition, we allow users to use `@staticInterop` // classes - with or without the `@anonymous` annotation. export 'dart:_js_annotations' show staticInterop, anonymous, JSExport; export 'dart:js_util' show NullRejectionException; /// An annotation on a JavaScript interop declaration. /// /// This annotation defines a given library, top-level external declaration, or /// extension type as a JavaScript interop declaration. /// /// Specifying [name] customizes the JavaScript name to use, which can be used /// in the following scenarios: /// /// - Adding a JavaScript prefix to all the external top-level declarations, /// static members, and constructors of a library by parameterizing the /// annotation on the library with [name]. /// - Specifying the JavaScript class to use for external static members and /// constructors of an interop extension type by parameterizing the annotation /// on the interop extension type with [name]. /// - Renaming external declarations by parameterizing the annotation on the /// member with [name]. /// /// In the case where [name] is not specified, the Dart name of the extension /// type or external declaration is used as the default. /// /// See https://dart.dev/interop/js-interop/usage#js for more details on how to /// use this annotation. /// /// > [!NOTE] /// > `package:js` exports an `@JS` annotation as well. Unlike that annotation, /// > this annotation applies to extension types, and will result in more /// > type-checking for external top-level declarations. class JS { final String? name; const JS([this.name]); } /// A non-nullish JavaScript value. /// /// A [JSAny] can be any JavaScript value except JavaScript `null` and /// `undefined`. JavaScript `null` and `undefined` are instead converted to Dart /// `null` by the compiler. Therefore, [JSAny]? is the top type of /// the type hierarchy as it includes nullish JavaScript values as well. extension type JSAny._(JSAnyRepType _jsAny) implements Object {} /// A JavaScript `Object`. /// /// [JSObject] is the supertype of all JavaScript objects, but not other JS /// types, like primitives. See https://dart.dev/interop/js-interop for more /// details on how to use JavaScript interop. /// /// When declaring interop extension types, [JSObject] is usually the type you /// will use as the representation type. @JS('Object') extension type JSObject._(JSObjectRepType _jsObject) implements JSAny { /// Creates a [JSObject] from an object provided by an earlier interop /// library. /// /// Accepts, for example, the types created using `package:js` or `dart:html`. /// /// This constructor is intended to allow users to avoid having to cast to and /// from [JSObject]. JSObject.fromInteropObject(Object interopObject) : _jsObject = interopObject as JSObjectRepType; /// Creates a new empty JavaScript object. /// /// The object is created using the JavaScript object initializer syntax /// (`{}`), and this constructor is more efficient than `{}.jsify()`. JSObject() : _jsObject = _createObjectLiteral(); } // TODO(srujzs): Move this member to `JSObject` once we can patch extension type // members. external JSObjectRepType _createObjectLiteral(); /// A JavaScript [`Function`](https://tc39.es/ecma262/#sec-function-objects) /// value. @JS('Function') extension type JSFunction._(JSFunctionRepType _jsFunction) implements JSObject {} /// A JavaScript callable function created from a Dart function. /// /// See [FunctionToJSExportedDartFunction.toJS] or /// [FunctionToJSExportedDartFunction.toJSCaptureThis] for more details on how /// to convert a Dart function. @JS('Function') extension type JSExportedDartFunction._( JSExportedDartFunctionRepType _jsExportedDartFunction ) implements JSFunction {} /// A JavaScript [`Array`](https://tc39.es/ecma262/#sec-array-objects). /// /// Because [JSArray] is an extension type, [T] is only a static guarantee and /// the array does not necessarily only contain [T] elements. For example: /// /// ```dart /// @JS() /// external JSArray get array; /// ``` /// /// `array` is not actually checked to ensure it contains instances of /// [JSNumber] when called. /// /// [T] may introduce additional checking elsewhere, however. When accessing /// elements of [JSArray] with type [T], there is a check to ensure the element /// is a [T] to ensure soundness. Similarly, when converting to a [List], /// casts may be introduced to ensure that it is indeed a [List]. @JS('Array') extension type JSArray._(JSArrayRepType _jsArray) implements JSObject { /// Creates an empty JavaScript `Array`. /// /// Equivalent to `new Array()` and more efficient than `[].jsify()`. external JSArray(); /// Creates a JavaScript `Array` of size [length] with no elements. external JSArray.withLength(int length); /// Creates a new, shallow-copied JavaScript `Array` instance from a /// JavaScript iterable or array-like object. @Since('3.6') external static JSArray from(JSObject arrayLike); /// The length in elements of this `Array`. @Since('3.6') external int get length; /// Sets the length in elements of this `Array`. /// /// Setting it smaller than the current length truncates this `Array`, and /// setting it larger adds empty slots, which requires [T] to be nullable. @Since('3.6') external set length(int newLength); /// The value at [position] in this `Array`. @Since('3.6') external T operator [](int position); /// Sets the [value] at [position] in this `Array`. @Since('3.6') external void operator []=(int position, T value); } /// A JavaScript `Promise` or a promise-like object. /// /// Because [JSPromise] is an extension type, [T] is only a static guarantee and /// the [JSPromise] may not actually resolve to a [T]. /// /// Also like with [JSArray], [T] may introduce additional checking elsewhere. /// When converted to a [Future], there is a cast to ensure that the [Future] /// actually resolves to a [T] to ensure soundness. @JS('Promise') extension type JSPromise._(JSPromiseRepType _jsPromise) implements JSObject { external JSPromise(JSFunction executor); } /// A Dart object that is wrapped with a JavaScript object so that it can be /// passed to JavaScript safely. /// /// Unlike [ExternalDartReference], this can be used as a JS type and is a /// subtype of [JSAny]. Users can also declare interop types using this as the /// representation type or declare interop members on this type. /// /// Use this interface when you want to pass Dart objects within the same /// runtime through JavaScript. There are no usable members in the resulting /// [JSBoxedDartObject]. /// /// See [ObjectToJSBoxedDartObject.toJSBox] to wrap an arbitrary [Object]. @JS('Object') extension type JSBoxedDartObject._(JSBoxedDartObjectRepType _jsBoxedDartObject) implements JSObject {} /// A JavaScript `ArrayBuffer`. @JS('ArrayBuffer') extension type JSArrayBuffer._(JSArrayBufferRepType _jsArrayBuffer) implements JSObject { /// Creates a JavaScript `ArrayBuffer` of size [length] using an optional /// [options] JavaScript object that sets the `maxByteLength`. @Since('3.6') external JSArrayBuffer(int length, [JSObject options]); } /// A JavaScript `DataView`. @JS('DataView') extension type JSDataView._(JSDataViewRepType _jsDataView) implements JSObject { /// Creates a JavaScript `DataView` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [byteLength]. @Since('3.6') external JSDataView(JSArrayBuffer buffer, [int byteOffset, int byteLength]); } /// Abstract supertype of all JavaScript typed arrays. extension type JSTypedArray._(JSTypedArrayRepType _jsTypedArray) implements JSObject {} /// A JavaScript `Int8Array`. @JS('Int8Array') extension type JSInt8Array._(JSInt8ArrayRepType _jsInt8Array) implements JSTypedArray { /// Creates a JavaScript `Int8Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Int8Array`. @Since('3.6') external JSInt8Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Int8Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSInt8Array.withLength(int length); } /// A JavaScript `Uint8Array`. @JS('Uint8Array') extension type JSUint8Array._(JSUint8ArrayRepType _jsUint8Array) implements JSTypedArray { /// Creates a JavaScript `Uint8Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Uint8Array`. @Since('3.6') external JSUint8Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Uint8Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSUint8Array.withLength(int length); } /// A JavaScript `Uint8ClampedArray`. @JS('Uint8ClampedArray') extension type JSUint8ClampedArray._( JSUint8ClampedArrayRepType _jsUint8ClampedArray ) implements JSTypedArray { /// Creates a JavaScript `Uint8ClampedArray` with [buffer] as its backing /// storage, offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Uint8ClampedArray`. @Since('3.6') external JSUint8ClampedArray([ JSArrayBuffer buffer, int byteOffset, int length, ]); /// Creates a JavaScript `Uint8ClampedArray` of size [length] whose elements /// are initialized to 0. @Since('3.6') external JSUint8ClampedArray.withLength(int length); } /// A JavaScript `Int16Array`. @JS('Int16Array') extension type JSInt16Array._(JSInt16ArrayRepType _jsInt16Array) implements JSTypedArray { /// Creates a JavaScript `Int16Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Int16Array`. @Since('3.6') external JSInt16Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Int16Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSInt16Array.withLength(int length); } /// A JavaScript `Uint16Array`. @JS('Uint16Array') extension type JSUint16Array._(JSUint16ArrayRepType _jsUint16Array) implements JSTypedArray { /// Creates a JavaScript `Uint16Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Uint16Array`. @Since('3.6') external JSUint16Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Uint16Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSUint16Array.withLength(int length); } /// A JavaScript `Int32Array`. @JS('Int32Array') extension type JSInt32Array._(JSInt32ArrayRepType _jsInt32Array) implements JSTypedArray { /// Creates a JavaScript `Int32Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Int32Array`. @Since('3.6') external JSInt32Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Int32Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSInt32Array.withLength(int length); } /// A JavaScript `Uint32Array`. @JS('Uint32Array') extension type JSUint32Array._(JSUint32ArrayRepType _jsUint32Array) implements JSTypedArray { /// Creates a JavaScript `Uint32Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Uint32Array`. @Since('3.6') external JSUint32Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Uint32Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSUint32Array.withLength(int length); } /// A JavaScript `Float32Array`. @JS('Float32Array') extension type JSFloat32Array._(JSFloat32ArrayRepType _jsFloat32Array) implements JSTypedArray { /// Creates a JavaScript `Float32Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Float32Array`. @Since('3.6') external JSFloat32Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Float32Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSFloat32Array.withLength(int length); } /// A JavaScript `Float64Array`. @JS('Float64Array') extension type JSFloat64Array._(JSFloat64ArrayRepType _jsFloat64Array) implements JSTypedArray { /// Creates a JavaScript `Float64Array` with [buffer] as its backing storage, /// offset by [byteOffset] bytes, of size [length]. /// /// If no [buffer] is provided, creates an empty `Float64Array`. @Since('3.6') external JSFloat64Array([JSArrayBuffer buffer, int byteOffset, int length]); /// Creates a JavaScript `Float64Array` of size [length] whose elements are /// initialized to 0. @Since('3.6') external JSFloat64Array.withLength(int length); } // The various JavaScript primitive types. Crucially, unlike the Dart type // hierarchy, none of these types are subtypes of [JSObject]. They are just // subtypes of [JSAny]. /// A JavaScript number. extension type JSNumber._(JSNumberRepType _jsNumber) implements JSAny {} /// A JavaScript boolean. extension type JSBoolean._(JSBooleanRepType _jsBoolean) implements JSAny {} /// A JavaScript string. extension type JSString._(JSStringRepType _jsString) implements JSAny {} /// A JavaScript `Symbol`. extension type JSSymbol._(JSSymbolRepType _jsSymbol) implements JSAny {} /// A JavaScript `BigInt`. extension type JSBigInt._(JSBigIntRepType _jsBigInt) implements JSAny {} /// An opaque reference to a Dart object that can be passed to JavaScript. /// /// The reference representation depends on the underlying platform. When /// compiling to JavaScript, a Dart object is a JavaScript object, and can be /// used directly without any conversions. When compiling to Wasm, an internal /// Wasm function is used to convert the Dart object to an opaque JavaScript /// value, which can later be converted back using another internal function. /// The underlying representation type is nullable, meaning a non-nullable /// [ExternalDartReference] may be `null`. /// /// This interface is a faster alternative to [JSBoxedDartObject] by not /// wrapping the Dart object with a JavaScript object. However, unlike /// [JSBoxedDartObject], this value belongs to the Dart runtime, and therefore /// can not be used as a JS type. This means users cannot declare interop types /// using this as the representation type or declare interop members on this /// type. This type is also not a subtype of [JSAny]. This type can only be used /// as parameter and return types of external JavaScript interop members or /// callbacks. Use [JSBoxedDartObject] to avoid those limitations. /// /// Besides these differences, [ExternalDartReference] operates functionally the /// same as [JSBoxedDartObject]. Use it to pass Dart objects within the same /// runtime through JavaScript. There are no usable members in the resulting /// [ExternalDartReference]. /// /// See [ObjectToExternalDartReference.toExternalReference] to allow an /// arbitrary value of type [T] to be passed to JavaScript. extension type ExternalDartReference._( ExternalDartReferenceRepType _externalDartReference ) {} /// JS type equivalent for `undefined` for interop member return types. /// /// Prefer using `void` instead of this. // TODO(srujzs): Mark this as deprecated. There are no performance costs from // using `void`, and we'll likely provide a different way to box `undefined`. typedef JSVoid = JSVoidRepType; /// Helper methods to determine if a value is JavaScript `undefined` or `null`. /// /// > [!NOTE] /// > The members within these extensions may throw depending on the platform. /// > Do not rely on them to be platform-consistent. /// /// JavaScript `undefined` and JavaScript `null` are internalized differently /// based on the backend. When compiling to JavaScript, Dart `null` can actually /// be JavaScript `undefined` or JavaScript `null`. When compiling to Wasm, /// that's not the case: there's only one Wasm value `null` can be. Therefore, /// when an interop API returns JavaScript `null` or JavaScript `undefined`, /// they are both converted to Dart `null` when compiling to Wasm, and when you /// pass a Dart `null` to an interop API, it is called with JavaScript `null`. /// When compiling to JavaScript, Dart `null` retains its original JavaScript /// value. Avoid writing code where this distinction between `null` and /// `undefined` matters. // TODO(srujzs): Investigate what it takes to allow users to distinguish between // the two "nullish" values. An annotation-based model where users annotate // interop APIs to internalize `undefined` differently seems promising, but does // not handle some cases like converting a `JSArray` with `undefined`s in it to // `List`. In this case, the implementation of the list wrapper needs to // make the decision, not the user. extension NullableUndefineableJSAnyExtension on JSAny? { /// Whether this value corresponds to JavaScript `undefined`. /// /// > [!NOTE] /// > Currently, there is no way to distinguish between JavaScript `undefined` /// > and JavaScript `null` when compiling to Wasm. Therefore, this getter /// > should only be used for code that compiles to JavaScript and will throw /// > when compiling to Wasm. external bool get isUndefined; /// Whether this value corresponds to JavaScript `null`. /// /// > [!NOTE] /// > Currently, there is no way to distinguish between JavaScript `undefined` /// > and JavaScript `null` when compiling to Wasm. Therefore, this getter /// > should only be used for code that compiles to JavaScript and will throw /// > when compiling to Wasm. external bool get isNull; bool get isUndefinedOrNull => this == null; bool get isDefinedAndNotNull => !isUndefinedOrNull; } /// Common utility functions that are useful for any JavaScript value. extension JSAnyUtilityExtension on JSAny? { /// Whether the result of `typeof` on this [JSAny]? is /// [typeString]. external bool typeofEquals(String typeString); /// Whether this [JSAny]? is an `instanceof` [constructor]. external bool instanceof(JSFunction constructor); /// Whether this [JSAny]? is an `instanceof` the constructor that /// is defined by [constructorName], which is looked up in the /// [globalContext]. /// /// If [constructorName] contains '.'s, the name is split into several parts /// in order to get the constructor. For example, `library1.JSClass` would /// involve fetching `library1` off of the [globalContext], and then fetching /// `JSClass` off of `library1` to get the constructor. /// /// If [constructorName] is empty or any of the parts or the constructor don't /// exist, returns false. bool instanceOfString(String constructorName) { if (constructorName.isEmpty) return false; final parts = constructorName.split('.'); JSObject? constructor = globalContext; for (final part in parts) { constructor = constructor?[part] as JSObject?; if (constructor == null) return false; } return instanceof(constructor as JSFunction); } /// Whether this [JSAny]? is an instance of the JavaScript type /// that is declared by [T]. /// /// Since the type-check this function emits is determined at compile-time, /// [T] needs to be an interop extension type that can also be determined at /// compile-time. In particular, `isA` can't be provided a generic type /// variable as a type argument. /// /// This method uses a combination of `null`, `typeof`, and `instanceof` /// checks in order to do this check. Use this instead of `is` checks. /// /// If [T] is a primitive JS type like [JSString], this uses a `typeof` check /// that corresponds to that primitive type like `typeofEquals('string')`. /// /// If [T] is a non-primitive JS type like [JSArray] or an interop extension /// type on one, this uses an `instanceof` check using the name or the /// @[JS] rename of the given type like /// `instanceOfString('Array')`. Note that if you rename the library using the /// @[JS] annotation, this uses the rename in the `instanceof` /// check like `instanceOfString('library1.JSClass')`. /// /// To determine the JavaScript constructor to use as the second operand in /// the `instanceof` check, this function uses the JavaScript name associated /// with the extension type, which is either the argument given to the /// @[JS] annotation or the Dart declaration name. So, if you had /// an interop extension type `JSClass` that wraps `JSArray` without a rename, /// this does an `instanceOfString('JSClass')` check and not an /// `instanceOfString('Array')` check. /// /// There are two exceptions to this rule. The first exception is /// `JSTypedArray`. As `TypedArray` does not exist as a property in /// JavaScript, this does some prototype checking to make `isA` /// do the right thing. The other exception is `JSAny`. If you do a /// `isA` check, it will only do a `null` check. /// /// Using this method with a [T] that has an object literal constructor will /// result in an error as you likely want to use [JSObject] instead. /// /// Using this method with a [T] that wraps a primitive JS type will result in /// an error telling you to use the primitive JS type instead. @Since('3.4') external bool isA(); /// Converts a JavaScript value to the Dart equivalent if possible. /// /// Effectively the inverse of [NullableObjectUtilExtension.jsify], [dartify] /// takes a JavaScript value and recursively converts it to a Dart object. /// Only JavaScript primitives, `Array`s, typed arrays, and map-like objects /// with string property names are supported. /// /// > [!NOTE] /// > Prefer using the specific conversion method like `toDart` if you know /// > the JavaScript type as this method may perform many type-checks. // TODO(srujzs): We likely need stronger tests for this method to ensure // consistency. external Object? dartify(); } /// Common utility functions for [Object]?s. extension NullableObjectUtilExtension on Object? { /// Converts a Dart object to the JavaScript equivalent if possible. /// /// Effectively the inverse of [JSAnyUtilityExtension.dartify], [jsify] takes /// a Dart object and recursively converts it to a JavaScript value. Only Dart /// primitives, [Iterable]s, typed lists, and [Map]s are supported. /// /// > [!NOTE] /// > Prefer using the specific conversion method like `toJS` if you know the /// > Dart type as this method may perform many type-checks. // TODO(srujzs): We likely need stronger tests for this method to ensure // consistency. external JSAny? jsify(); } /// Utility extensions for [JSFunction]. // TODO(srujzs): We may want to provide a syntax for users to avoid `.call` and // directly call the function in JavaScript using `(...)`. extension JSFunctionUtilExtension on JSFunction { /// Call this [JSFunction] using the JavaScript `.call` syntax and returns the /// result. /// /// Takes at most 4 args for consistency with other APIs and relative brevity. /// If more are needed, you can declare your own external member with the same /// syntax. // We rename this function since declaring a `call` member makes a class // callable in Dart. This is convenient, but unlike Dart functions, JavaScript // functions explicitly take a `this` argument (which users can provide `null` // for in the case where the function doesn't need it), which may lead to // confusion. // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call @JS('call') external JSAny? callAsFunction([ JSAny? thisArg, JSAny? arg1, JSAny? arg2, JSAny? arg3, JSAny? arg4, ]); } // 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(srujzs): Move some of these to the associated extension type. /// Conversions from [JSExportedDartFunction] to [Function]. extension JSExportedDartFunctionToFunction on JSExportedDartFunction { /// The Dart [Function] that this [JSExportedDartFunction] wrapped. /// /// Must be a function that was wrapped with /// [FunctionToJSExportedDartFunction.toJS] or /// [FunctionToJSExportedDartFunction.toJSCaptureThis]. external Function get toDart; } /// Conversions from [Function] to [JSExportedDartFunction]. extension FunctionToJSExportedDartFunction on Function { /// A callable JavaScript function that wraps this [Function]. /// /// If the static type of the [Function] could not be determined or if /// the static type uses types that are disallowed, the call will fail to /// compile. See /// https://dart.dev/interop/js-interop/js-types#requirements-on-external-declarations-and-function-tojs /// for more details on what types are allowed. /// /// The max number of arguments that are passed to this [Function] from the /// wrapper JavaScript function is determined by this [Function]'s static /// type. Any extra arguments passed to the JavaScript function after the max /// number of arguments are discarded like they are with regular JavaScript /// functions. /// /// Calling this on the same [Function] again will always result in a new /// JavaScript function. external JSExportedDartFunction get toJS; /// A callable JavaScript function that wraps this [Function] and captures the /// `this` value when called. /// /// Identical to [toJS], except the resulting [JSExportedDartFunction] will /// pass `this` from JavaScript as the first argument to the converted /// [Function]. Any [Function] that is converted with this member should take /// in an extra parameter at the beginning of the parameter list to handle /// this. @Since('3.6') external JSExportedDartFunction get toJSCaptureThis; } /// Conversions from [JSBoxedDartObject] to [Object]. extension JSBoxedDartObjectToObject on JSBoxedDartObject { /// The Dart [Object] that this [JSBoxedDartObject] wrapped. /// /// Throws an [Exception] if the Dart runtime was not the same as the one in /// which the [Object] was wrapped or if this was not a wrapped Dart [Object]. external Object get toDart; } /// Conversions from [Object] to [JSBoxedDartObject]. extension ObjectToJSBoxedDartObject on Object { /// A JavaScript object that wraps this [Object]. /// /// There are no usable members in the resulting [JSBoxedDartObject] and you /// may get a new [JSBoxedDartObject] when calling [toJSBox] on the same Dart /// [Object]. /// /// Throws an [Exception] if this [Object] is a JavaScript value. /// /// Unlike [ObjectToExternalDartReference.toExternalReference], this returns a /// JavaScript value. Therefore, the representation is guaranteed to be /// consistent across all platforms and interop members can be declared on /// [JSBoxedDartObject]s. external JSBoxedDartObject get toJSBox; } /// Conversions from [ExternalDartReference] to the value of type [T]. extension ExternalDartReferenceToObject on ExternalDartReference { /// The Dart value of type [T] that this [ExternalDartReference] refers to. /// /// When compiling to JavaScript, a Dart object is a JavaScript object, and /// therefore this directly returns the Dart object. When compiling to Wasm, /// an internal Wasm function is used to convert the opaque JavaScript value /// to the original Dart object. external T get toDartObject; } /// Conversions from a value of type [T] to [ExternalDartReference]. extension ObjectToExternalDartReference on T { /// An opaque reference to this value of type [T] which can be passed to /// JavaScript. /// /// When compiling to JavaScript, a Dart object is a JavaScript object, and /// therefore this directly returns the Dart object. When compiling to Wasm, /// an internal Wasm function is used to convert the Dart object to an opaque /// JavaScript value. If this value is `null`, returns `null`. /// /// A value of type [ExternalDartReference] should be treated as completely /// opaque. It can only be passed around as-is or converted back using /// [ExternalDartReferenceToObject.toDartObject]. /// /// When this getter is called multiple times on the same Dart object, the /// underlying references in the resulting [ExternalDartReference]s are /// guaranteed to be equal. Therefore, `==` will always return true between /// such [ExternalDartReference]s. However, like JS types, `identical` between /// such values may return different results depending on the compiler. external ExternalDartReference get toExternalReference; } /// Conversions from [JSPromise] to [Future]. extension JSPromiseToFuture on JSPromise { /// A [Future] that either completes with the result of the resolved /// [JSPromise] or propagates the error that the [JSPromise] rejected with. external Future get toDart; } /// Conversions from [Future] to [JSPromise] where the [Future] returns a value. extension FutureOfJSAnyToJSPromise on Future { /// A [JSPromise] that either resolves with the result of the completed /// [Future] or rejects with an object that contains its error. /// /// The rejected object contains the original error as a [JSBoxedDartObject] /// in the property `error` and the original stack trace as a [String] in the /// property `stack`. JSPromise get toJS { return JSPromise( (JSFunction resolve, JSFunction reject) { this.then( (JSAny? value) { resolve.callAsFunction(resolve, value); return value; }, onError: (Object error, StackTrace stackTrace) { // TODO(srujzs): Can we do something better here? This is pretty much // useless to the user unless they call a Dart callback that consumes // this value and unboxes. final errorConstructor = globalContext['Error'] as JSFunction; final wrapper = errorConstructor.callAsConstructor( "Dart exception thrown from converted Future. Use the properties " "'error' to fetch the boxed error and 'stack' to recover " "the stack trace." .toJS, ); wrapper['error'] = error.toJSBox; wrapper['stack'] = stackTrace.toString().toJS; reject.callAsFunction(reject, wrapper); return wrapper; }, ); }.toJS, ); } } /// Conversions from [Future] to [JSPromise] where the [Future] does not return /// a value. extension FutureOfVoidToJSPromise on Future { /// A [JSPromise] that either resolves once this [Future] completes or rejects /// with an object that contains its error. /// /// The rejected object contains the original error as a [JSBoxedDartObject] /// in the property `error` and the original stack trace as a [String] in the /// property `stack`. JSPromise get toJS { return JSPromise( (JSFunction resolve, JSFunction reject) { this.then( (_) => resolve.callAsFunction(resolve), onError: (Object error, StackTrace stackTrace) { // TODO(srujzs): Can we do something better here? This is pretty much // useless to the user unless they call a Dart callback that consumes // this value and unboxes. final errorConstructor = globalContext['Error'] as JSFunction; final wrapper = errorConstructor.callAsConstructor( "Dart exception thrown from converted Future. Use the properties " "'error' to fetch the boxed error and 'stack' to recover " "the stack trace." .toJS, ); wrapper['error'] = error.toJSBox; wrapper['stack'] = stackTrace.toString().toJS; reject.callAsFunction(reject, wrapper); }, ); }.toJS, ); } } /// Conversions from [JSArrayBuffer] to [ByteBuffer]. extension JSArrayBufferToByteBuffer on JSArrayBuffer { /// Converts this [JSArrayBuffer] to a [ByteBuffer] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [ByteBuffer]s are [JSArrayBuffer]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSArrayBuffer] will affect the [ByteBuffer] and /// vice versa. external ByteBuffer get toDart; } /// Conversions from [ByteBuffer] to [JSArrayBuffer]. extension ByteBufferToJSArrayBuffer on ByteBuffer { /// Converts this [ByteBuffer] to a [JSArrayBuffer] by either casting, /// unwrapping, or cloning the [ByteBuffer]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [ByteBuffer] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [ByteBuffer]'s values into a new /// > [JSArrayBuffer]. /// > Avoid assuming that modifications to this [ByteBuffer] will affect the /// > [JSArrayBuffer] and vice versa unless it was instantiated in JavaScript. external JSArrayBuffer get toJS; } /// Conversions from [JSDataView] to [ByteData]. extension JSDataViewToByteData on JSDataView { /// Converts this [JSDataView] to a [ByteData] by either casting or wrapping /// it. /// /// When compiling to JavaScript, [ByteData]s are [JSDataView]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSDataView] will affect the [ByteData] and vice /// versa. external ByteData get toDart; } /// Conversions from [ByteData] to [JSDataView]. extension ByteDataToJSDataView on ByteData { /// Converts this [ByteData] to a [JSDataView] by either casting, unwrapping, /// or cloning the [ByteData]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [ByteData] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [ByteData]'s values into a new /// > [JSDataView]. /// > Avoid assuming that modifications to this [ByteData] will affect the /// > [JSDataView] and vice versa unless it was instantiated in JavaScript. external JSDataView get toJS; } /// Conversions from [JSInt8Array] to [Int8List]. extension JSInt8ArrayToInt8List on JSInt8Array { /// Converts this [JSInt8Array] to a [Int8List] by either casting or wrapping /// it. /// /// When compiling to JavaScript, [Int8List]s are [JSInt8Array]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSInt8Array] will affect the [Int8List] and vice /// versa. external Int8List get toDart; } /// Conversions from [Int8List] to [JSInt8Array]. extension Int8ListToJSInt8Array on Int8List { /// Converts this [Int8List] to a [JSInt8Array] by either casting, /// unwrapping, or cloning the [Int8List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Int8List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Int8List]'s values into a new /// > [JSInt8Array]. /// > Avoid assuming that modifications to this [Int8List] will affect the /// > [JSInt8Array] and vice versa unless it was instantiated in JavaScript. external JSInt8Array get toJS; } /// Conversions from [JSUint8Array] to [Uint8List]. extension JSUint8ArrayToUint8List on JSUint8Array { /// Converts this [JSUint8Array] to a [Uint8List] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [Uint8List]s are [JSUint8Array]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSUint8Array] will affect the [Uint8List] and vice /// versa. external Uint8List get toDart; } /// Conversions from [Uint8List] to [JSUint8Array]. extension Uint8ListToJSUint8Array on Uint8List { /// Converts this [Uint8List] to a [JSUint8Array] by either casting, /// unwrapping, or cloning the [Uint8List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Uint8List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Uint8List]'s values into a new /// > [JSUint8Array]. /// > Avoid assuming that modifications to this [Uint8List] will affect the /// > [JSUint8Array] and vice versa unless it was instantiated in JavaScript. external JSUint8Array get toJS; } /// Conversions from [JSUint8ClampedArray] to [Uint8ClampedList]. extension JSUint8ClampedArrayToUint8ClampedList on JSUint8ClampedArray { /// Converts this [JSUint8ClampedArray] to a [Uint8ClampedList] by either /// casting or wrapping it. /// /// When compiling to JavaScript, [Uint8ClampedList]s are /// [JSUint8ClampedArray]s and this operation will be a cast. When compiling /// to Wasm, a wrapper is introduced. Modifications to this /// [JSUint8ClampedArray] will affect the [Uint8ClampedList] and vice versa. external Uint8ClampedList get toDart; } /// Conversions from [Uint8ClampedList] to [JSUint8ClampedArray]. extension Uint8ClampedListToJSUint8ClampedArray on Uint8ClampedList { /// Converts this [Uint8ClampedList] to a [JSUint8ClampedArray] by either /// casting, unwrapping, or cloning the [Uint8ClampedList]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Uint8ClampedList] may or may not be a /// > wrapper depending on if it was converted from JavaScript or instantiated /// > in Dart. If it's a wrapper, this method unwraps it. If it's instantiated /// > in Dart, this method clones this [Uint8ClampedList]'s values into a new /// > [JSUint8ClampedArray]. /// > Avoid assuming that modifications to this [Uint8ClampedList] will affect /// > the [JSUint8ClampedArray] and vice versa unless it was instantiated in /// > JavaScript. external JSUint8ClampedArray get toJS; } /// Conversions from [JSInt16Array] to [Int16List]. extension JSInt16ArrayToInt16List on JSInt16Array { /// Converts this [JSInt16Array] to a [Int16List] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [Int16List]s are [JSInt16Array]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSInt16Array] will affect the [Int16List] and vice /// versa. external Int16List get toDart; } /// Conversions from [Int16List] to [JSInt16Array]. extension Int16ListToJSInt16Array on Int16List { /// Converts this [Int16List] to a [JSInt16Array] by either casting, /// unwrapping, or cloning the [Int16List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Int16List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Int16List]'s values into a new /// > [JSInt16Array]. /// > Avoid assuming that modifications to this [Int16List] will affect the /// > [JSInt16Array] and vice versa unless it was instantiated in JavaScript. external JSInt16Array get toJS; } /// Conversions from [JSUint16Array] to [Uint16List]. extension JSUint16ArrayToInt16List on JSUint16Array { /// Converts this [JSUint16Array] to a [Uint16List] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [Uint16List]s are [JSUint16Array]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSUint16Array] will affect the [Uint16List] and /// vice versa. external Uint16List get toDart; } /// Conversions from [Uint16List] to [JSUint16Array]. extension Uint16ListToJSInt16Array on Uint16List { /// Converts this [Uint16List] to a [JSUint16Array] by either casting, /// unwrapping, or cloning the [Uint16List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Uint16List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Uint16List]'s values into a new /// > [JSUint16Array]. /// > Avoid assuming that modifications to this [Uint16List] will affect the /// > [JSUint16Array] and vice versa unless it was instantiated in JavaScript. external JSUint16Array get toJS; } /// Conversions from [JSInt32Array] to [Int32List]. extension JSInt32ArrayToInt32List on JSInt32Array { /// Converts this [JSInt32Array] to a [Int32List] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [Int32List]s are [JSInt32Array]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSInt32Array] will affect the [Int32List] and vice /// versa. external Int32List get toDart; } /// Conversions from [Int32List] to [JSInt32Array]. extension Int32ListToJSInt32Array on Int32List { /// Converts this [Int32List] to a [JSInt32Array] by either casting, /// unwrapping, or cloning the [Int32List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Int32List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Int32List]'s values into a new /// > [JSInt32Array]. /// > Avoid assuming that modifications to this [Int32List] will affect the /// > [JSInt32Array] and vice versa unless it was instantiated in JavaScript. external JSInt32Array get toJS; } /// Conversions from [JSUint32Array] to [Uint32List]. extension JSUint32ArrayToUint32List on JSUint32Array { /// Converts this [JSUint32Array] to a [Uint32List] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [Uint32List]s are [JSUint32Array]s and this /// operation will be a cast. When compiling to Wasm, a wrapper is introduced. /// Modifications to this [JSUint32Array] will affect the [Uint32List] and /// vice versa. external Uint32List get toDart; } /// Conversions from [Uint32List] to [JSUint32Array]. extension Uint32ListToJSUint32Array on Uint32List { /// Converts this [Uint32List] to a [JSUint32Array] by either casting, /// unwrapping, or cloning the [Uint32List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Uint32List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Uint32List]'s values into a new /// > [JSUint32Array]. /// > Avoid assuming that modifications to this [Uint32List] will affect the /// > [JSUint32Array] and vice versa unless it was instantiated in JavaScript. external JSUint32Array get toJS; } /// Conversions from [JSFloat32Array] to [Float32List]. extension JSFloat32ArrayToFloat32List on JSFloat32Array { /// Converts this [JSFloat32Array] to a [Float32List] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [Float32List]s are [JSFloat32Array]s and /// this operation will be a cast. When compiling to Wasm, a wrapper is /// introduced. Modifications to this [JSFloat32Array] will affect the /// [Float32List] and vice versa. external Float32List get toDart; } /// Conversions from [Float32List] to [JSFloat32Array]. extension Float32ListToJSFloat32Array on Float32List { /// Converts this [Float32List] to a [JSFloat32Array] by either casting, /// unwrapping, or cloning the [Float32List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Float32List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Float32List]'s values into a new /// > [JSFloat32Array]. /// > Avoid assuming that modifications to this [Float32List] will affect the /// > [JSFloat32Array] and vice versa unless it was instantiated in /// > JavaScript. external JSFloat32Array get toJS; } /// Conversions from [JSFloat64Array] to [Float64List]. extension JSFloat64ArrayToFloat64List on JSFloat64Array { /// Converts this [JSFloat64Array] to a [Float64List] by either casting or /// wrapping it. /// /// When compiling to JavaScript, [Float64List]s are [JSFloat64Array]s and /// this operation will be a cast. When compiling to Wasm, a wrapper is /// introduced. Modifications to this [JSFloat64Array] will affect the /// [Float64List] and vice versa. external Float64List get toDart; } /// Conversions from [Float64List] to [JSFloat64Array]. extension Float64ListToJSFloat64Array on Float64List { /// Converts this [Float64List] to a [JSFloat64Array] by either casting, /// unwrapping, or cloning the [Float64List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, all typed lists are the equivalent /// > JavaScript typed arrays, and therefore this method simply casts. /// > When compiling to Wasm, this [Float64List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [Float64List]'s values into a new /// > [JSFloat64Array]. /// > Avoid assuming that modifications to this [Float64List] will affect the /// > [JSFloat64Array] and vice versa unless it was instantiated in /// > JavaScript. external JSFloat64Array get toJS; } /// Conversions from [JSArray] to [List]. extension JSArrayToList on JSArray { /// Converts this [JSArray] to a [List] by either casting or wrapping it. /// /// When compiling to JavaScript, [List]s are [JSArray]s and this will be a /// cast. When compiling to Wasm, a wrapper is introduced. Modifications to /// this [JSArray] will affect the [List] and vice versa. In order to ensure /// type soundness, this method may introduce casts when accessing elements in /// order to ensure they are of type [T]. external List get toDart; } /// Conversions from [List] to [JSArray]. extension ListToJSArray on List { /// Converts this [List] to a [JSArray] by either casting, unwrapping, or /// cloning the [List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, the core [List] is a JavaScript `Array`, /// > and therefore this method simply casts. User-defined [List]s are /// > currently unsupported when compiling to JavaScript. /// > When compiling to Wasm, this [List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method clones this [List]'s values into a new [JSArray]. /// > Avoid assuming that modifications to this [List] will affect the /// > [JSArray] and vice versa unless it was instantiated in JavaScript. external JSArray get toJS; /// Converts this [List] to a [JSArray] by either casting, unwrapping, or /// proxying the [List]. /// /// > [!NOTE] /// > Depending on whether code is compiled to JavaScript or Wasm, this /// > conversion will have different semantics. /// > When compiling to JavaScript, the core [List] is a JavaScript `Array`, /// > and therefore this method simply casts. User-defined [List]s are /// > currently unsupported when compiling to JavaScript. /// > When compiling to Wasm, this [List] may or may not be a wrapper /// > depending on if it was converted from JavaScript or instantiated in /// > Dart. If it's a wrapper, this method unwraps it. If it's instantiated in /// > Dart, this method proxies the [List] using a heavyweight `Array` /// > wrapper. Access to the original [List]'s elements may be very /// > unperformant. /// > Modifications to this [List] will affect the [JSArray] and vice versa. external JSArray get toJSProxyOrRef; } /// Conversions from [JSNumber] to [double] or [int]. extension JSNumberToNumber on JSNumber { /// Converts this [JSNumber] to a [double]. external double get toDartDouble; /// Converts this [JSNumber] to an [int]. /// /// If this [JSNumber] is not an integer value, throws. external int get toDartInt; } /// Conversions from [double] to [JSNumber]. extension DoubleToJSNumber on double { /// Converts this [double] to a [JSNumber]. external JSNumber get toJS; } /// Conversions from [num] to [JSNumber]. extension NumToJSExtension on num { /// Converts this [num] to a [JSNumber]. JSNumber get toJS => DoubleToJSNumber(toDouble()).toJS; } /// Conversions from [JSBoolean] to [bool]. extension JSBooleanToBool on JSBoolean { /// Converts this [JSBoolean] to a [bool]. external bool get toDart; } /// Conversions from [bool] to [JSBoolean]. extension BoolToJSBoolean on bool { /// Converts this [bool] to a [JSBoolean]. external JSBoolean get toJS; } /// Conversions from [JSString] to [String]. extension JSStringToString on JSString { /// Converts this [JSString] to a [String]. external String get toDart; } /// Conversions from [String] to [JSString]. extension StringToJSString on String { /// Converts this [String] to a [JSString]. external JSString get toJS; } /// General-purpose JavaScript operators. /// /// Indexing operators (`[]`, `[]=`) should be declared through operator /// overloading instead like: /// ``` /// external operator int [](int key); /// ``` /// /// All operators in this extension shall accept and return only JS types. // TODO(srujzs): Add more as needed. For now, we just expose the ones needed to // migrate from `dart:js_util`. extension JSAnyOperatorExtension on JSAny? { // Arithmetic operators. /// The result of `this` + [any] in JavaScript. external JSAny add(JSAny? any); /// The result of `this` - [any] in JavaScript. external JSAny subtract(JSAny? any); /// The result of `this` * [any] in JavaScript. external JSAny multiply(JSAny? any); /// The result of `this` / [any] in JavaScript. external JSAny divide(JSAny? any); /// The result of `this` % [any] in JavaScript. external JSAny modulo(JSAny? any); /// The result of `this` ** [any] in JavaScript. external JSAny exponentiate(JSAny? any); // Comparison operators. /// The result of `this` > [any] in JavaScript. external JSBoolean greaterThan(JSAny? any); /// The result of `this` >= [any] in JavaScript. external JSBoolean greaterThanOrEqualTo(JSAny? any); /// The result of `this` < [any] in JavaScript. external JSBoolean lessThan(JSAny? any); /// The result of `this` <= [any] in JavaScript. external JSBoolean lessThanOrEqualTo(JSAny? any); /// The result of `this` == [any] in JavaScript. external JSBoolean equals(JSAny? any); /// The result of `this` != [any] in JavaScript. external JSBoolean notEquals(JSAny? any); /// The result of `this` === [any] in JavaScript. external JSBoolean strictEquals(JSAny? any); /// The result of `this` !== [any] in JavaScript. external JSBoolean strictNotEquals(JSAny? any); // Bitwise operators. /// The result of `this` >>> [any] in JavaScript. // TODO(srujzs): This should return `num` or `double` instead. external JSNumber unsignedRightShift(JSAny? any); // Logical operators. /// The result of `this` && [any] in JavaScript. external JSAny? and(JSAny? any); /// The result of `this` || [any] in JavaScript. external JSAny? or(JSAny? any); /// The result of !`this` in JavaScript. external JSBoolean get not; /// The result of !!`this` in JavaScript. external JSBoolean get isTruthy; } /// The global scope that is used to find user-declared interop members. /// /// For example: /// /// ``` /// @JS() /// external String get name; /// ``` /// /// Reading `name` will execute JavaScript code like `globalContext.name`. /// /// There are subtle differences depending on the compiler, but in general, /// [globalContext] can be treated like JavaScript's `globalThis`. external JSObject get globalContext; /// Given a instance of a Dart class that contains an @[JSExport] /// annotation, creates a JavaScript object that wraps the given Dart object. /// /// The object literal will be a map of properties, which are either the written /// instance member names or their renames, to callbacks that call the /// corresponding Dart instance members. /// /// See https://dart.dev/interop/js-interop/mock for more details on how to /// declare classes that can be used in this method. external JSObject createJSInteropWrapper(T dartObject); // TODO(srujzs): Expose this method when we handle conformance checking for // interop extension types. We don't expose this method today due to the bound // on `T`. `@staticInterop` types can't implement `JSObject`, so this method // simply wouldn't work. We could make it extend `Object` to support the // `@staticInterop` case, but if we ever refactor to `extends JSObject`, this // would be a breaking change. For now, due to the low usage of // `createStaticInteropMock`, we avoid introducing this method until later. // external T createJSInteropMock( // U dartMock, [JSObject? proto = null]); /// Dynamically imports a JavaScript module with the given [moduleName] using /// the JavaScript `import()` syntax. /// /// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import /// for more details. /// /// Returns a [JSPromise] that resolves to a [JSObject] that's the module /// namespace object. external JSPromise importModule(JSAny moduleName);