TypeScript para NodeJS usando Visual Studio Code

nodejs-typescript.png

Como todos bien sabéis, JavaScript es un lenguaje de programación débilmente tipado. Esto quiere decir que nosotros como desarrolladores nunca tenemos que inferir a una variable, una constante o un parámetro un tipo especial para que el compilador lo interprete, si no que el propio compilador o intérprete va a inferir el tipo por medio del contexto en el que se encuentre una variable.

Esto que nos permite desarrollar de una manera flexible y dinámica, pues no tenemos que dedicar tiempo a estar definiendo estructuras de datos complejas para nuestra aplicación. A su vez nos perjudica a la larga a la hora de evolucionar y mantener nuestros aplicativos.

Es por eso que si queremos usar JavaScript en la parte del servidor de una manera responsable, tengamos que tomar medidas para ello. Según que contextos, quiero empezar a desarrollar mis APIS y mis aplicaciones en NodeJS de una forma más robusta y tipada. De esta manera podré refactorizar código de mi backend de una manera mucho más segura ya que los tipos son una buena herramienta para los editores ya que gracias a ellos se basan para indicarnos si existen fallos de escritura de código en nuestro sistema, algo que con un sistema débilmente tipado no es posible al no poder saber el tipo de la variable hasta que nos encontramos en tiempo de ejecución.

Para cumplir con este nuevo propósito, voy a empezar a aprender sobre TypeScript. TypeScript es lenguaje de programación tipado que nos permite transpilar a JavaScript. Es una solución parecida a la propuesta por BabelJS, pero convirtiendo TypeScript por JavaScript. TypeScript está desarrollado y mantenido por el equipo de Microsoft y fue creado para cumplir dos objetivos: tipar JavaScript y mejorar aquellas funcionalidades de JavaScript que sufren un fallo de diseño o que todavía no han sido implementadas en el lenguaje, pero si se encuentran especificadas en su estándar.

Lo primero que quiero hacer es encontrar una herramienta potente que me ayude a desarrollar mi backend en NodeJS con TypeScript como lenguaje. El intérprete de NodeJS solo sabe interpretar JavaScript y cómo bien me contó Micael Gallego en Twitter, no tiene sentido que NodeJS interprete TypeScript en el futuro, por lo tanto necesitaremos de una herramienta que cumpla con dos funcionalidades prioritarias para mi:

  • El poder transpilar el código de TypeScript en JavaScript de una manera transparente para el desarrollador
  • La posibilidad de poder depurar en la base de código escrita en TypeScript.

La mejor herramienta que he encontrado para cumplir con mis exigencias ha sido Visual Studio Code, el editor ligero desarrollado por Microsoft que tan bien implementa todo aquello que tiene que ver con JavaScript, NodeJS y TypeScript.

Vamos a crear un pequeño “Hola Mundo” para mostrar la potencia de la herramienta. En el camino explicaremos algunas configuraciones que tendremos que hacer para que todo funcione correctamente.

Primeros pasos

Lo primero que vamos a hacer es instalar todo lo necesario en nuestro ordenador para que el proyecto acabe funcionando. Necesitamos:

Lo siguiente será instalar el transpilador de TypeScript. Como casi todo lo que rodea a NodeJS, este presenta una pequeña CLI para que podamos trabajar desde el terminal de una manera sencilla. Para instalarlo lo hacemos así:

npm install -g tsc

Si prefieres que vaya como dependencia de tu paquete sería así:

npm install tsc --save-dev

Una vez que tengamos esto, crearemos un proyecto de NodeJS. Para eso lanzamos los siguientes comandos en terminal:

mkdir hello-world-node-ts
cd hello-world-node-ts
npm init -- contestamos a las preguntas que nos propone NodeJS

Lo siguiente que quiero haces es crear una estructura de carpetas. Como no quiero que mis TS se mezclen con los JS generados voy a tener esta estructura:

-- build
   -- app.js
   -- utils.js  
-- src
   -- app.ts
   -- utils.ts
-- package.json

El desarrollador trabajará sobre la carpeta src y build será autogenerada por el propio transpilador de TypeScript, ‘tsc’.

El código de estos ficheros .ts va a ser muy sencillo ya que por ahora no queremos aprender sobre TypeScript sino de cómo configurarlo. Es este:

// utils.ts
export function getHelloName(name: String): String {
    return `Hello ${name}`;
}
// app.ts
import { getHelloName } from './utils';
console.log(getHelloName('Walter White'));

He dividido el ejemplo en dos ficheros para que veamos como TypeScript se comporta a la hora de transpilar los módulos típicos de NodeJS que tanto le caracterizan y que no son tan vistos en el front.Lo único que hace el app.ts es importar la función de ‘utils’ y ejecutarla pesándola un nombre.

Ya solo con este ejemplo podemos ver dos de las funcionalidades que nos da el editor:

  • El intellisense de funciones:

captura-de-pantalla-de-2016-09-15-15-37-22

  • Avisarnos de que existe un error si indico el tipo correcto para el parámetro de entrada de la función:

captura-de-pantalla-de-2016-09-15-15-37-53

Lo veréis muy potente si estáis acostumbrados a trabajar solo con JavaScript en estos editores. Ahora es mucho más parecido a lo que ofrecen IDES como Visual Studio o IntelliJ.

Configurando el proyecto

Una vez que hemos escrito nuestro código ejemplo, va a ser configurar el proyecto para que transpile correctamente el ejemplo. Esos .ts no son posible ejecutar por NodeJS por loq ue necesitamos indicarle al ‘tsc’ como tiene que comportarse con nuestro proyecto.

Lo que hacemos es incluir un fichero llamado tsconfig.json que el ‘tsc’ siempre leerá para transpilar como necesitamos. Lo indicamos con los siguientes parámetros:

// tsconfig.json
{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "sourceMap": true,
        "watch": true,
        "outDir": "build"
    },
    "exclude": [
        "node_modules"
    ]
}

Los explicamos a continuación:

  • module: dentro de las opciones de configuración podemos indicar cómo sistema de módulos queremos que genere tsc. Cómo estamos trabajando en NodeJS y este usa el sistema de módulos de commonjs usamos ese. Tenemos más opciones que dependiendo el contexto nos puede ser útiles.
  • target: indicamos la especificación de EcmaScript que queremos satisfacer. Aunque NodeJS ya tiene soporte para ES6 yo recomiendo usar todavía la ES5 para que sea compatible con todas las versiones de NodeJS.
  • sourceMap: Nos genera un fichero sourceMap por cada .js transpilado que apunta al fichero .ts original. Necesario si queremos tener depuración de la aplicación.
  • watch: si indicamos a true este parámetro haremos que una vez que se lance el proceso de transpilación, siempre se ‘autotranspile’ cada vez que se guarde un nuevo fichero. Es nuestro observador particular.
  • outDir: Nos permite indicar la ruta donde queremos guardas los compilados. En este caso guardaremos los ficheros en la carpeta ‘build’ que se autogenerará al transpilar.
  • exclude: Nos permite excluir directorios de la compilación. De esta manera podemos indicar que node_modules no es necesario que vuelva a transpilar. Ahorramos tiempos.

Si necesitas incluir alguna opción más esta es la documentación oficial.

Creando una tarea automática en Visual Studio Code

Una vez que tenemos configurado nuestro proyecto, es hora de compilar nuestro proyecto. Esto lo podemos hacer de dos maneras:

  1. Desde el terminal, puestos en la ruta raíz del proyecto indicando el siguiente comando ‘tsc -p’.
  2. Usando el propio Visual Studio Code podemos indicar una tarea automatizada. Por medio de la interfaz podemos generar una siguiendo estos pasos:
    1. Pulsa los comando Ctrl + Shift + P.
    2. Nos saldrá un buscador, si indicamos ‘Tarea’, nos saldrán opciones sobre tareas.
    3. Seleccionamos ‘Tareas: Configurar ejecutor de tareas’.
    4. Nos dirán todos los gestores que puedo configurar con VSC, indicamos TypeScript -tsconfig.json.
    5. Se nos generará un fichero tasks.json en la carpeta .vscode como el siguiente:
// .vscode/tasks.json
{
    "version": "0.1.0",
    "command": "tsc",
    "isShellCommand": true,
    "args": ["-p", "."],
    "showOutput": "silent",
    "problemMatcher": "$tsc"
}

A partir de ahora ya podemos ejecutar la tarea en Visual Studio Code. La forma más rápida de ejecutarlo será por medio de la combinación Ctrl + Shift + B. De esta manera comprobareis que en la esquina izquierda se queda una barra como de cargando, esto se quedará siempre así ahora ya que el ‘watch’ va a estar observando nuestro código .ts para volver a compilar en cualquier momento.

También aparecerá la carpeta ‘build’ tal y como indicamos antes. Veréis que en esta carpeta hay dos ficheros .js (utils.js y app.js) y dos ficheros .map (utils.js.map y app.js.map). Los js que nos han generado son estos:

// utils.js
function getHelloName(name) {
    return "Hello " + name;
}
exports.getHelloName = getHelloName;
//# sourceMappingURL=utils.js.map
// app.js
var utils_1 = require('./utils');
console.log(utils_1.getHelloName('Walter White'));
//# sourceMappingURL=app.js.map

Como veis ahora todo es código JavaScript, sin tipos y con el ‘require’ típico de NodeJS. Los comentarios  del final apuntan al sourceMap que ‘tsc’ nos ha generado. Todo muy sencillo.

Si ahora desde el terminal vamos hasta la carpeta build y ejecutamos ‘node app.js’, la consola nos pintará un bonito “Hello Walter White’.

Depurando nuestros ficheros .ts

Una vez que ya tenemos todo el flujo de desarrollo creado, solo nos falta incluir el flujo de depuración. Me gusta Visual Studio Code porque te permite depurar aplicaciones NodeJS por medio de interfaz gráfica pudiendo ver valores y flujos.

Si veis los iconos de la izquierda y pulsáis sobre el icono de una señal de prohibido con un bug como el de la imagen:

captura-de-pantalla-de-2016-09-15-15-34-26

Entráis en la funcionalidad de depuración. Si pulsáis sobre el ‘play’ e indicáis que queréis depurar una aplicación de NodeJS, Visual Studio Code os genera un fichero en la carpeta .vscode llamado launch.json con una configuración base de depuración. El fichero es parecido a este:

// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
    {
        "name": "Iniciar",
        "type": "node",
        "request": "launch",
        "program": "${workspaceRoot}/build/app.js",
        "stopOnEntry": false,
        "args": [],
        "cwd": "${workspaceRoot}",
        "preLaunchTask": null,
        "runtimeExecutable": null,
        "runtimeArgs": [
            "--nolazy"
        ],
        "env": {
            "NODE_ENV": "development"
        },
        "externalConsole": false,
        "sourceMaps": true,
        "outDir": "${workspaceRoot}/build"
     },
     {
        "name": "Asociar",
        "type": "node",
        "request": "attach",
        "port": 5858,
        "address": "localhost",
        "restart": false,
        "sourceMaps": false,
        "outDir": null,
        "localRoot": "${workspaceRoot}",
        "remoteRoot": null
     },
     {
        "name": "Asociar al proceso",
        "type": "node",
        "request": "attach",
        "processId": "${command.PickProcess}",
        "port": 5858,
        "sourceMaps": false,
        "outDir": null
     }
  ]
}

De este fichero nos interesa, del nodo configuración el primero objeto, el que tiene como “name”,  Iniciar. De este objeto tenemos que fijarnos en 3 atributos:

  1. “program”: “${workspaceRoot}/build/app.js”: Program indica donde empieza tu aplicación NodeJS. En este caso como nuestra app está en la carpeta ‘build’ tenemos que indicárselo.
  2. “sourceMaps”: true: a true para indicar que la depuración tiene que estar pendiente de los ficheros maps
  3. “outDir”: “${workspaceRoot}/build”: para indicar donde están los ficheros de salida.

Si ponemos un punto de parada en la función de ‘utils.ts’ y pulsamos sobre el play del depurador, vemos que la aplicación nos detiene la ejecución en el breakpoint incluido.

Conclusión

Con todo esto tenemos un ciclo más o menos completo. Podríamos cambiar las tareas de VSC por unas de Gulp o Grunt para no depender del editor y seguiríamos trabajando igual. Creo que merece la pena realizar esto para conseguir aplicaciones mejores y mantenibles.

Durante los próximos meses investigaré y aprenderé sobre TypeScript, si encuentro algo reseñable, no dudéis que lo compartiré con vosotros.

Nos leemos 🙂

Imagen de portada | Gerald Pereira

Anuncios

2 comments

  1. Pingback: 4 razones que cambiarán tu visión de Microsoft | el.abismo = de[null]
  2. Carlos Lugo · abril 14

    Gracias por el tutorial, muy util aunque ya está un poco desactualizado. En la nueva versión de Visual Studio Code (Abril 2017) fue necesario agregar lo siguiente en launch.json (para que se detuviera en los breakpoint):
    “sourceMaps”: true,
    “outFiles”: [ “${workspaceRoot}/build/*.js” ]

    Aunque como soy nuevo en esto todavía tengo mis dudas si todo está funcionando como debería.

    Me gusta

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s