Saltar al contenido principal

Interop con JS del pasado

Archivo del soporte anterior de interoperabilidad con JS de Dart.

La evolución de la interoperabilidad con JavaScript

#

Dart 3.3 introduce una nueva generación de JS interop que ofrece un conjunto unificado de características y APIs para acceder a las funcionalidades de JavaScript y del navegador dentro de tu código Dart. Este enfoque moderno mejora la experiencia del desarrollador y habilita el soporte de WebAssembly (Wasm), alineando a Dart con el futuro de la web.

La siguiente tabla mapea las nuevas soluciones de JS y de interoperabilidad web de Dart a sus contrapartes anteriores:

Las iteraciones anteriores de JS interop para Dart se consideran heredadas (legacy) y están depreciadas a partir de Dart 3.7 (febrero de 2025). Prefiere usar dart:js_interop en adelante y migra los usos de las antiguas librerías de interoperabilidad cuando sea posible. El soporte para las APIs del navegador, como dart:html, ahora está soportado por package:web.

dart:js

#

dart:js exponía un objeto envoltura (object wrapper) concreto para interoperar con objetos de JS. Esta envoltura contenía métodos basados en cadenas (Strings) para obtener, establecer y llamar dinámicamente propiedades en el objeto de JS envuelto. Tenía un menor rendimiento debido a los costos de envoltura y era ergonómicamente más difícil de usar. Por ejemplo, no tenías autocompletado de código ya que no podías declarar miembros de interoperabilidad y, en su lugar, dependías de cadenas (Strings). Muchas de las funcionalidades expuestas en dart:js como allowInterop fueron reexpuestas posteriormente a través de otras librerías de interoperabilidad.

Esta librería se considera heredada (legacy) desde que se lanzaron package:js y dart:js_util.

package:js

#

package:js introdujo la funcionalidad para declarar tipos y miembros de interoperabilidad. Permitía a los usuarios escribir clases de interoperabilidad en lugar de extension types de interoperabilidad. En tiempo de ejecución, estas clases se borraban (erased) a un tipo que es similar a JSObject de dart:js_interop.

dart
@JS()
class JSType {}

Los usuarios de package:js encontrarán familiar la sintaxis y semántica de dart:js_interop. Es posible que puedas migrar a dart:js_interop reemplazando la definición de la clase por un extension type y hacer que funcione en muchos casos.

Sin embargo, existen diferencias significativas:

  • Los tipos de package:js no se podían usar para interoperar con las APIs del navegador. Los tipos de dart:js_interop sí pueden.
  • package:js permitía el despacho dinámico (dynamic dispatch). Esto significaba que si convertías el tipo de package:js a dynamic y llamabas a un miembro de interoperabilidad en él, este se reenviaría al miembro correcto. Esto ya no es posible con dart:js_interop.
  • La anotación @JS de package:js no tiene garantías de solidez (soundness) ya que los tipos de retorno de los miembros external no se comprobaban. dart:js_interop es sólido (sound).
  • Los tipos de package:js no podían renombrar miembros de instancia o tener miembros que no fueran external.
  • Los tipos de package:js podían ser subtipos y supertipos de clases que no eran de interoperabilidad. Esto se usaba a menudo para los mocks (simulaciones). Con dart:js_interop, la simulación se realiza sustituyendo el objeto de JS en su lugar. Consulta el tutorial sobre simulación (mocking).
  • Los tipos @anonymous eran una forma de declarar un tipo de interoperabilidad con un constructor de objeto literal. dart:js_interop no distingue los tipos de esta manera y cualquier constructor external con argumentos nombrados es un constructor de objeto literal.

@staticInterop

#

Junto con @JS y @anonymous, package:js expuso más tarde @staticInterop, que era un prototipo de los extension types de interoperabilidad. Es tan expresivo y restrictivo como dart:js_interop y estaba pensado como una sintaxis de transición hasta que los extension types estuvieran disponibles.

Los tipos @staticInterop se borraban implícitamente a JSObject. Requería que los usuarios declararan todos los miembros de instancia en extensiones para que solo se pudiera usar la semántica estática, y tenía mayores garantías de solidez (soundness). Los usuarios podían usarlo para interactuar con las APIs del navegador, y también permitía cosas como renombrar y miembros no external. Al igual que los extension types de interoperabilidad, no tenía soporte para despacho dinámico (dynamic dispatch).

Las clases @staticInterop casi siempre se pueden migrar a un extension type de interoperabilidad simplemente cambiando la clase a un extension type y eliminando las anotaciones.

dart:js_interop expuso @staticInterop (y @anonymous, pero solo si también se usa @staticInterop) para admitir la semántica de interoperabilidad estática hasta que se agregaron los extension types al lenguaje. Todos estos tipos ahora deberían migrarse a extension types.

dart:js_util

#

dart:js_util proporcionaba una serie de funciones de utilidad que no podían ser declaradas en un tipo de package:js o que eran necesarias para pasar valores de un lado a otro. Esto incluía miembros como:

  • allowInterop (que ahora es Function.toJS)
  • getProperty/setProperty/callMethod/callConstructor (que ahora están en dart:js_interop_unsafe)
  • Varios operadores de JS
  • Ayudantes para la comprobación de tipos
  • Soporte para simulación (mocking)
  • Y más.

dart:js_interop y dart:js_interop_unsafe contienen estos ayudantes (helpers) ahora con una sintaxis posiblemente alternativa.