VOLVER

Primera semana de codificación

6 min de lectura

La primera semana del periodo de codificación en GSoC 2016, así que empecé el proceso de actualización de Jangouts de Angular 1.x a Angular 2. Esta semana, las tareas se han cumplido dentro del plazo, espero seguir así para la próxima semana.

Estoy siguiendo la guía de actualización de la documentación oficial de Angular, que tiene dos bloques principales:

  • Preparación
  • Actualización con el Adaptador de Actualización

Acabo de terminar el bloque de preparación. Tuve suerte porque el código de Jangouts es realmente claro y sigue dos puntos importantes del bloque de preparación: Seguir la guía de estilo de Angular y usar directivas de componentes. Así que esto me dejó solo dos cosas por hacer. Primero de todo, cambiar de cargar archivos con etiquetas <script> a un cargador de módulos y luego migrar el código de JavaScript a TypeScript. Pero lo he hecho en orden inverso, primero migrar a TypeScript y luego cambiar a un cargador de módulos. ¿Por qué? Creo que esta forma es más "natural", al menos para este proyecto.

Migrando a TypeScript

Jangouts tiene un sistema de construcción gulp funcionando, así que no tuve que preocuparme sobre cómo se cargan los scripts. Primero me preocupé por migrar los archivos a TypeScript y cuando todo estuvo listo, entonces intenté sacar provecho de la sintaxis import de TypeScript/ES6.

Migrar código de JavaScript a TypeScript es realmente fácil, solo necesitas cambiar la extensión de .js a .ts. Obviamente, el sistema gulp existente no funciona con estos cambios, así que en paralelo a ejecutar gulp deberías ejecutar tsc --watch src/**/*.ts. Este comando muestra muchos errores pero no te preocupes por esto si el código JavaScript es correcto, todos estos errores están relacionados con el proceso de comprobación de tipos del compilador TypeScript.

Durante este proceso de migración, también realicé algunos cambios en el código para hacerlo más modular. Jangouts tiene todos los componentes registrados en el mismo módulo Angular janusHangouts y en otros proyectos en los que trabajé antes aprendí que esto puede causar algunos problemas cuando tienes que hacer pruebas unitarias, así que defino un módulo diferente para cada componente (janusHangouts.componentName) y luego lo hago una dependencia del módulo principal. Esto tiene dos ventajas: hacer tests es más fácil y en el futuro podemos cargar componentes bajo demanda con un cargador de módulos (pero no estoy seguro sobre el último).

Volviendo a lo que dije antes, al compilar el código JavaScript con tsc esto muestra muchos errores. Uno que podría aparecer mucho es algo como esto:

error TS7006: Parameter '$state' implicitly has an 'any' type.

Esto es porque el compilador TypeScript quiere un tipo definido para todas las variables, pero podemos hacer que tsc establezca el tipo implícito any para variables sin tipo. Lo único que hay que hacer es deshabilitar la opción noImplicitAny en el archivo tsconfig.json.

Otro error que podemos encontrar al trabajar con elementos HTML es:

error TS2339: Property 'muted' does not exist on type 'HTMLElement'.

Este error se produce por un código como este:

var video = $('video', element)[0];
video.muted = true;

El error se produce porque TypeScript es seguro en tipos. Así que el $('video', element)[0] devuelve el tipo HTMLElement que no contiene una propiedad muted. El subtipo HTMLVideoElement sin embargo contiene la propiedad muted. Así que la solución es castear el resultado a HTMLVideoElement así:

var video = <HTMLVideoElement>$('video', element)[0];
video.muted = true;

Finalmente, otro error común es:

error TS2339: Property 'id' does not exist on type '{}'.

Este es otro "problema" producido por las validaciones de tipos de TypeScript. Podemos encontrar errores como ese en fragmentos de código similares a:

var room = {};
// Algún código aquí...
function isRoom(room) {
return room.id == roomId;
}

Para resolver esto y hacer el código menos propenso a errores, deberíamos definir una interfaz para el objeto room.

interface Room {
id?: number; // ? hace el atributo opcional
}
// Algún código aquí ...
var room: Room = {};
// Algún código aquí...
function isRoom(room: Room) {
return room.id == roomId;
}

Usando un Cargador de Módulos

¿Por qué deberíamos usar un cargador de módulos? Podemos encontrar la respuesta en el sitio de Angular:

Usar un cargador de módulos como SystemJS, Webpack, o Browserify nos permite usar los sistemas de módulos integrados de los lenguajes TypeScript o ES2015 en nuestras aplicaciones. Podemos usar las características de import y export que explícitamente especifican qué código puede y será compartido entre diferentes partes de la aplicación. [...]

Cuando luego llevamos nuestras aplicaciones a producción, los cargadores de módulos también hacen más fácil empaquetarlas todas en paquetes de producción con baterías incluidas.

Descarto Browserify porque tuve malas experiencias en el pasado. Así que solo he probado con SystemJS y Webpack.

SystemJS

SystemJS parece realmente limpio, realmente simple. Lo único que tuve que hacer es definir un punto de entrada (normalmente el archivo principal de la aplicación) y la sintaxis import hace el resto. Así que, si tenemos las declaraciones import correctamente colocadas, todo funciona sin problema.

Pero con esta solución, necesitamos conservar gulp, porque SystemJS solo se preocupa por las importaciones. Así que esto implica añadir el compilador TypeScript a gulp y deshabilitar la auto inyección de script en html.

Sinceramente no probé eso antes de intentar reescribir la configuración de gulp quería echar un vistazo a Webpack primero.

Webpack

La configuración de Webpack es más compleja que SystemJS, pero da una sustitución de gulp. Como hace SystemJS, necesitamos definir un punto de entrada para la aplicación y también decirle dónde está el index.html para incluir los archivos JavaScript.

Inicialmente, tuve algunos problemas, pero después de mirar algunos ejemplos, tuve una versión funcional. Explorando más a fondo en Webpack encontré algo que me hace elegirlo antes que otros: podemos hacer import o require de archivos no JavaScript. Así que podemos hacer cosas como requerir la plantilla de una directiva Angular, y en el proceso de construcción esta plantilla se incluirá como una variable string dentro del componente, y con estilos tenemos la misma característica. Esta funcionalidad mejora la construcción final de la aplicación porque todos los archivos necesitados por un componente se colocarán dentro del archivo JavaScript del componente, mejorando el rendimiento pero sin dificultar programarlo.

Una cosa más

Este verano parece emocionante con todas las cosas que tengo que aprender con GSoC. Si quieres seguir el progreso de lo que estoy haciendo mantente actualizado de este blog o sigue mis contribuciones en GitHub. También, publiqué un tablero de Trello con la planificación y cosas por hacer con este proyecto (no completamente actualizado todavía pero en progreso).