Tipos de JS
Información de uso sobre los tipos principales en JS interop.
Los valores de Dart y los valores de JS pertenecen a dominios de lenguaje separados. Al compilar a
Wasm, también se ejecutan en entornos de ejecución (runtimes) separados. Por lo tanto, debes tratar los valores de JS
como tipos externos. Para proporcionar tipos de Dart para los valores de JS,
dart:js_interop
expone un conjunto de tipos con el prefijo JS llamados "tipos de JS".
Estos tipos se utilizan para distinguir entre valores de Dart y valores de JS en tiempo de
compilación.
Es importante destacar que estos tipos se materializan (reified) de manera diferente según se compile a
Wasm o a JS. Esto significa que su tipo en tiempo de ejecución diferirá y, por lo tanto,
no puedes usar comprobaciones is ni conversiones de tipo (casts) as.
Para interactuar con estos valores de JS y examinarlos, debes usar
miembros de interoperabilidad external o
conversiones.
Jerarquía de tipos
#Los tipos de JS forman una jerarquía de tipos natural:
-
Top type:
JSAny, which is any non-nullish JS value- Primitivos:
JSNumber,JSBoolean,JSString,JSSymbol,JSBigInt JSObject, which is any JS objectJSFunctionJSExportedDartFunction, que representa un callback de Dart que se convirtió en una función de JS
JSArrayJSPromiseJSDataViewJSTypedArray- Arrays tipados de JS como
JSUint8Array
- Arrays tipados de JS como
JSBoxedDartObject, which allows users to box and pass Dart values opaquely within the same Dart runtime- Desde Dart 3.4 en adelante, el tipo
ExternalDartReferenceendart:js_interoptambién permite a los usuarios pasar valores de Dart de forma opaca, pero no es un tipo de JS. Obtén más información sobre las ventajas y desventajas de cada opción aquí.
- Desde Dart 3.4 en adelante, el tipo
- Primitivos:
Puedes encontrar la definición de cada tipo en la documentación de la API de dart:js_interop.
Conversiones
#Para usar un valor de un dominio a otro, probablemente querrás convertir el
valor al tipo correspondiente del otro dominio. Por ejemplo, podrías querer
convertir una List<JSString> de Dart en un array de cadenas de JS, lo cual está
representado por el tipo de JS JSArray<JSString>, para que puedas pasar el array
a una API de JS interop.
Dart proporciona una serie de miembros de conversión en varios tipos de Dart y tipos de JS para realizar la conversión de los valores entre dominios por ti.
Los miembros que convierten valores de Dart a JS suelen comenzar con toJS:
String str = 'hello world';
JSString jsStr = str.toJS;
Los miembros que convierten valores de JS a Dart suelen comenzar con toDart:
JSNumber jsNum = ...;
int integer = jsNum.toDartInt;
No todos los tipos de JS tienen una conversión, y no todos los tipos de Dart tienen una conversión. Generalmente, la tabla de conversión se ve como la siguiente:
Tipo de dart:js_interop | Tipo de Dart |
|---|---|
JSNumber, JSBoolean, JSString |
num, int, double, bool, String |
JSExportedDartFunction | Function |
JSArray<T extends JSAny?> |
List<T extends JSAny?> |
JSPromise<T extends JSAny?> |
Future<T extends JSAny?> |
Arrays tipados como JSUint8Array |
Listas tipadas de dart:typed_data |
JSBoxedDartObject | Valor de Dart opaco |
ExternalDartReference | Valor de Dart opaco |
Requisitos en declaraciones external y Function.toJS
#
Para garantizar la seguridad de tipos y la consistencia, el compilador impone requisitos
sobre qué tipos pueden fluir hacia y desde JS. No se permite pasar valores arbitrarios de Dart a JS.
En su lugar, el compilador requiere que los usuarios utilicen un tipo de interoperabilidad compatible,
ExternalDartReference, o un primitivo, que luego sería convertido implícitamente
por el compilador. Por ejemplo, se permitiría lo siguiente:
@JS()
external void primitives(String a, int b, double c, num d, bool e);
@JS()
external JSArray jsTypes(JSObject _, JSString __);
extension type InteropType(JSObject _) implements JSObject {}
@JS()
external InteropType get interopType;
@JS()
external void externalDartReference(ExternalDartReference _);
Mientras que estos devolverían un error:
@JS()
external Function get function;
@JS()
external set list(List _);
Estos mismos requisitos existen cuando utilizas Function.toJS
para hacer que una
función de Dart sea invocable en JS. Los valores que fluyen hacia y desde este callback deben
ser un tipo de interoperabilidad compatible o un primitivo.
Si usas un primitivo de Dart como String, se produce una conversión implícita en el
compilador para convertir ese valor de un valor de JS a un valor de Dart. Si el rendimiento
es crítico y no necesitas examinar el contenido de la cadena, entonces usar
JSString en su lugar para evitar el costo de la conversión puede tener sentido, como en el
segundo ejemplo.
Compatibilidad, comprobaciones de tipo y conversiones de tipo (casts)
#El tipo en tiempo de ejecución de los tipos de JS puede diferir según el compilador. Esto afecta
las comprobaciones de tipo en tiempo de ejecución y las conversiones de tipo (casts). Por lo tanto, casi siempre evita las comprobaciones is
donde el valor es un tipo de interoperabilidad o donde el tipo de destino es un tipo de interoperabilidad:
void f(JSAny a) {
if (a is String) { … }
}
void f(JSAny a) {
if (a is JSObject) { … }
}
Además, evita las conversiones de tipo (casts) entre tipos de Dart y tipos de interoperabilidad:
void f(JSString s) {
s as String;
}
Para comprobar el tipo de un valor de JS, usa un miembro de interoperabilidad como typeofEquals
o
instanceOfString
que examine el propio valor de JS:
void f(JSAny a) {
// Here `a` is verified to be a JS function, so the cast is okay.
if (a.typeofEquals('function')) {
a as JSFunction;
}
}
Desde Dart 3.4 en adelante, puedes usar la función auxiliar isA
para comprobar si
un valor es cualquier tipo de interoperabilidad:
void f(JSAny a) {
if (a.isA<JSString>()) {} // `typeofEquals('string')`
if (a.isA<JSArray>()) {} // `instanceOfString('Array')`
if (a.isA<CustomInteropType>()) {} // `instanceOfString('CustomInteropType')`
}
Dependiendo del parámetro de tipo, transformará la llamada en la comprobación de tipo adecuada para ese tipo.
Para evitar comprobaciones de tipo no válidas con tipos de JS interop,
habilita la regla de linter invalid_runtime_check_with_js_interop_types.
null vs undefined
#
JS tiene tanto un valor null como un valor undefined. Esto contrasta con Dart,
que solo tiene null. Con el fin de hacer que los valores de JS sean más ergonómicos de usar, si un
miembro de interoperabilidad devolviera un valor null o undefined de JS, el compilador mapea
estos valores al null de Dart. Por lo tanto, un miembro como value
en el siguiente ejemplo puede interpretarse como que devuelve un objeto JS, null de JS o undefined:
@JS()
external JSObject? get value;
Si el tipo de retorno no se declaró como anulable, el programa lanzará un
error si el valor devuelto fuera null o undefined de JS para garantizar la solidez (soundness).
JSBoxedDartObject vs ExternalDartReference
#
Desde Dart 3.4 en adelante, tanto JSBoxedDartObject
como ExternalDartReference
pueden usarse para pasar referencias opacas a Objects de Dart a través de JavaScript.
Sin embargo, JSBoxedDartObject envuelve la referencia opaca en un objeto de JavaScript,
mientras que ExternalDartReference es la referencia en sí y, por lo tanto, no es un tipo de JS.
Usa JSBoxedDartObject si necesitas un tipo de JS o si necesitas comprobaciones adicionales para
asegurarte de que los valores de Dart no se pasen a otro entorno de ejecución (runtime) de Dart. Por ejemplo, si
el objeto de Dart debe colocarse en un JSArray o pasarse a una API que
acepta un JSAny, usa JSBoxedDartObject. Usa ExternalDartReference
en caso contrario, ya que será más rápido.
Consulta toExternalReference
y toDartObject
para realizar la conversión hacia y desde una
ExternalDartReference.
A menos que se indique lo contrario, la documentación en este sitio refleja Dart 3.12.2. Página actualizada por última vez el 2026-05-15. Ver fuente oreportar un problema.