Después de comentar en el artículo anterior algunos de los hitos importantes en la historia de Dart, junto con las características y herramientas básicas del lenguaje, ahora toca repasar algunas de las novedades específicas más relevantes de la recién liberada versión 2.0.0.

  • El sistema de tipos ahora es más estricto que en la versión anterior. Utilizando una combinación de comprobaciones realizadas tanto en tiempo de compilación como en tiempo de ejecución se garantiza que el valor asignado a una variable o parámetro es del tipo declarado para los mismos, elevándose un error en caso contrario. Con este cambio Dart se aleja de su sistema de tipos inicial, más parecido al de JavaScript, que realiza conversiones de los valores de un tipo a otro de manera automática y sin indicación explícita por parte de los desarrolladores. El nuevo sistema de tipos permite desarrollar sistemas con comportamientos más predecibles y facilita la detección temprana de errores.
  • El tipo entero int tiene ahora un tamaño fijo predefinido de 64 bits. En la versión anterior el tipo no tenía un tamaño definido y las variables enteras podían tener cualquier valor, Dart reservaba más memoria para ellas a medida que su valor crecía. Ahora los enteros son de tamaño fijo y la funcionalidad anterior está en la clase BigInt. Tipo este último que se ha añadido de forma nativa a JavaScript hace relativamente poco tiempo para representar enteros con una precisión de tamaño arbitrario.
  • Se ha eliminado la necesidad de utilizar new y const para instanciar objetos. Su uso está marcado como deprecado, pero se sigue soportando. Este cambio equipara los constructores con las factorías, haciéndolos patrones intercambiables e indistinguibles a la hora de invocarlos. Además tiene la ventaja de que no es necesario utilizar const de forma repetitiva cuando en una sentencia se construye un objeto constante por composición de otros objetos constantes.
  • Las funciones marcadas con async se ejecutan ahora de forma síncrona hasta alcanzar la primera llamada a await dentro del cuerpo de dichas funciones. En la versión anterior la ejecución de las funciones asíncronas se realizaba en el siguiente ciclo de ejecución del event loop de Dart empezando desde el principio del cuerpo de dichas funciones. Con este cambio el comportamiento es más acorde a lo que la intuición sugiere y no se presupone la necesidad de conocer el detalle de funcionamiento interno de la máquina virtual para programar correctamente una función de manera asíncrona.
  • Las directivas part of ahora pueden utilizarse con una cadena de texto con la ruta de un fichero en vez del nombre de una librería. Esto permite crear librerías anónimas y facilita organizar los proyectos de forma interna con una mayor granularidad. La sintaxis original ha sido marcada como deprecada.
  • El tipo void se ha promocionado y ahora puede utilizarse como tipo parametrizado dentro de expresiones genéricas. Es decir, ahora por ejemplo puede escribirse Future<void>. Sin embargo, no puede utilizarse dentro de todas las expresiones, por lo que T is void, T == void o identical(T, void) no son expresiones válidas en tiempo de compilación. Mi impresión general es que este cambio se ha publicado sin estar completamente definido, y en consecuencia implementado y probado. Y si ese es el comportamiento definido por diseño entonces el cambio se me antoja un tanto extraño.
  • Cuando se invoca un método que no se encuentra implementado, como puede llegar a ocurrir en el caso de métodos de clases abstractas, Dart invoca a un método con un nombre prefijado llamado noSuchMethod. En la versión anterior de Dart el método no recibía ningún parámetro opcional de la función original no implementada. En la nueva versión recibe los parámetros opcionales con sus valores por defecto.
  • Se han dejado de soportar los paquetes dart:isolate  y dart:mirrors para la parte cliente web, aunque aún se siguen soportando para la parte servidora ejecutándose sobre la máquina virtual en línea de comandos. Con respecto a dart:isolate, se recomienda el uso de webworkers para ejecutar tareas en segundo plano en el navegador. Y con respecto a dart:mirrors, el mecanismo de reflexión siempre ha sido una patata caliente en Dart, y parece que con esta versión va a seguir siéndolo. Habrá que esperar a la siguiente.
  • Los nombres de constantes de todas las librerías se han renombrado para utilizar minúsculas en vez de mayúsculas. Escribir las constantes utilizando mayúsculas es una vieja práctica que se sigue utilizando dentro las guías de estilo de la mayoría de lenguajes de programación. El cambio es una propuesta de hace un tiempo y que se ha materializado en esta nueva versión de Dart. Su intención es que si una variable deja de ser una constante no debería implicar reescribir todo el código que hace referencia a ella.
  • Las librerías internas del core del SDK han sufrido muchos cambios. Se han añadido y modificado clases y métodos. Enumero a continuación algunos de estos cambios más representativos. En el paquete dart:async, a la clase Stream se le han añadido los métodos cast y castFrom para realizar comprobaciones en tiempo de ejecución de conversiones de objetos de un tipo a otro. En el paquete dart:collection, a la mayoría de las clases se les ha añadido el constructor of para facilitar la instanciación de colecciones a partir de iterables. En el paquete dart:convert, la mayoría de conversores retorna ahora  Uint8List en vez de List<int>. En el paquete dart:core, a los iterables se les ha añadido el método whereType para facilitar el filtrado de secuencias heterogéneas de objetos. Y en el paquete dart:typed_data, se ha añadido una clase de vista inmutable para todos los tipos de listas existentes.
  • El cambio más relevante del formateador de código es que ahora formatea las expresiones interpoladas dentro de las cadenas de texto. Y que se le ha incorporado el parámetro --fix en línea de comandos para ayudar con la migración del código existente a la nueva versión de Dart. El nuevo parámetro hace que el formateador elimine automáticamente new y const al instanciar objetos, y sustituye : por = al asignar valores por defecto a los parámetros opcionales.
  • El analizador de código ahora sólo funciona en modo estricto y trata el directorio packages como cualquier otro directorio ordinario. Estas dos modificaciones reflejan dos cambios importantes realizados en la nueva versión de Dart. El cambio referido al modo estricto se refiere al nuevo sistema de tipos, y el cambio referido al directorio packages se refiere a que en la versión anterior el directorio era en realidad un enlace simbólico que se utilizaba internamente para la resolución de paquetes y requería un tratamiento especial.
  • El gestor de dependencias se ha reescrito y ahora dispone de un sistema de construcción y publicación más flexible que los antiguos transformers, que han dejado de ser soportados. Además, ahora al publicar un paquete se obliga a declarar la versión máxima de SDK soportada. A los paquetes existentes que no tienen declarada ninguna versión máxima se les aplica el valor <2.0.0 por defecto.
  • El compilador cruzado de Dart a JavaScript ahora genera código para la versión 2 de Dart, lo que implica que el código generado tiene un tamaño mayor que en la versión anterior debido al mayor número de comprobaciones que se realizan en el sistema de tipos. Para tratar de aliviar este efecto se le ha añadido el parámetro --omit-implicit-checks al compilador con objeto de eliminar dichas comprobaciones del código generado para los casos en que no consideren realmente necesarias en tiempo de ejecución. Otro cambio significativo es que la clase Promise de JavaScript ahora se expone con un mecanismo compatible con la clase Future de Dart.
  • Dartium, la versión de Chrome que incluía la máquina virtual de Dart embebida, ha dejado de desarrollarse y dársele soporte.

Además de todos estos puntos comentados, se han corregido muchos casos de uso poco habituales del sistema de tipos y de la ejecución asíncrona de métodos. La release notes correspondiente contiene un listado muy detallado y su lectura es obligada para obtener una visión más completa de todas las modificaciones introducidas.