blob: f5eb1869e9d40417d68abc9766e8ec0cdcfe4126 [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.
/// Utility methods to manipulate JavaScript objects dynamically.
///
/// This library is typically meant to be used when the names of properties or
/// methods are not known statically. This library is similar to `dart:js_util`,
/// except the methods here are extension methods that use JS types. This
/// allows code using these functions to also be compiled to WebAssembly.
///
/// In general, prefer to write JS interop interfaces and external static
/// interop members using `dart:js_interop`. This library is meant to work
/// around issues and help with migration from older JS interop libraries.
///
/// > [!NOTE]
/// > As the name suggests, usage of this library *can* be unsafe. This means
/// > that safe usage of these methods cannot necessarily be verified
/// > statically. Prefer using statically analyzable values like constants or
/// > literals for property or method names so that usage can be verified. This
/// > library should be used cautiously and only when the same effect cannot be
/// > achieved with static interop.
///
/// {@category Web}
library;
import 'dart:js_interop';
/// Utility methods to check, get, set, and call properties on a [JSObject].
///
/// See the [JavaScript specification](https://tc39.es/ecma262/#sec-object-type)
/// for more details on using properties.
extension JSObjectUnsafeUtilExtension on JSObject {
/// Shorthand helper for [hasProperty] to check whether this [JSObject]
/// contains the property key [property], but takes and returns a Dart value.
bool has(String property) => hasProperty(property.toJS).toDart;
/// Whether or not this [JSObject] contains the property key [property].
external JSBoolean hasProperty(JSAny property);
/// Shorthand helper for [getProperty] to get the value of the property key
/// [property] of this [JSObject], but takes and returns a Dart value.
JSAny? operator [](String property) => getProperty(property.toJS);
/// The value of the property key [property] of this [JSObject].
external R getProperty<R extends JSAny?>(JSAny property);
/// Shorthand helper for [setProperty] to write the [value] of the property
/// key [property] of this [JSObject], but takes a Dart value.
void operator []=(String property, JSAny? value) =>
setProperty(property.toJS, value);
/// Write the [value] of property key [property] of this [JSObject].
external void setProperty(JSAny property, JSAny? value);
external JSAny? _callMethod(JSAny method,
[JSAny? arg1, JSAny? arg2, JSAny? arg3, JSAny? arg4]);
/// Calls [method] on this [JSObject] with up to four arguments.
///
/// Returns the result of calling [method], which must be an [R].
///
/// This helper doesn't allow passing nulls, as it determines whether an
/// argument is passed based on whether it was null or not. Prefer
/// [callMethodVarArgs] if you need to pass nulls.
R callMethod<R extends JSAny?>(JSAny method,
[JSAny? arg1, JSAny? arg2, JSAny? arg3, JSAny? arg4]) =>
_callMethod(method, arg1, arg2, arg3, arg4) as R;
external JSAny? _callMethodVarArgs(JSAny method, [List<JSAny?>? arguments]);
/// Calls [method] on this [JSObject] with a variable number of [arguments].
///
/// Returns the result of calling [method], which must be an [R].
R callMethodVarArgs<R extends JSAny?>(JSAny method,
[List<JSAny?>? arguments]) =>
_callMethodVarArgs(method, arguments) as R;
/// Deletes the property with key [property] from this [JSObject].
external JSBoolean delete(JSAny property);
}
/// Utility methods to call [JSFunction]s as constructors.
extension JSFunctionUnsafeUtilExtension on JSFunction {
external JSObject _callAsConstructor(
[JSAny? arg1, JSAny? arg2, JSAny? arg3, JSAny? arg4]);
/// Calls this [JSFunction] as a constructor with up to four arguments.
///
/// Returns the constructed object, which must be an [R].
///
/// This helper doesn't allow passing nulls, as it determines whether an
/// argument is passed based on whether it was null or not. Prefer
/// [callAsConstructorVarArgs] if you need to pass nulls.
// TODO(srujzs): The type bound should extend `JSObject`.
R callAsConstructor<R>(
[JSAny? arg1, JSAny? arg2, JSAny? arg3, JSAny? arg4]) =>
_callAsConstructor(arg1, arg2, arg3, arg4) as R;
external JSObject _callAsConstructorVarArgs([List<JSAny?>? arguments]);
/// Calls this [JSFunction] as a constructor with a variable number of
/// arguments.
///
/// Returns the constructed [JSObject], which must be an [R].
R callAsConstructorVarArgs<R extends JSObject>([List<JSAny?>? arguments]) =>
_callAsConstructorVarArgs(arguments) as R;
}