Alias para rutas con typescript y node

Normalmente al hacer buen uso de los patrones de diseño de Software nos encontramos la mayoría de las veces, dependiendo del tamaño del proyecto, con un enramado de rutas que hace poco elegante el acceso a nuestros módulos. Por ejemplo tomemos el siguiente caso:

> dist/
> node_modules/
> src/
  > modules
    > moduleA/
      > index.ts
  > services/
    > config/
      > index.ts
> package.json
> tsconfig.json

Supongamos que deseamos acceder al servicio de configuración desde el módulo A, lo cuál nos daría el siguiente import

import * as Config from '../../services/config';

Aunque este es un ejemplo relativamente sencillo podemos comenzar ya a notar que la navegación de las rutas carece de una forma elegante de ser resuelta y puede complicarse de forma relativamente fácil.

1. Paths en Typescript

Afortunadamente Typescript cuenta con un conjunto de opciones de configuración que permiten establecer alias o abreviaciones para las rutas de nuestro proyecto. Concretamente typescript reorganiza el mapeo de la búsqueda a través de la entrada paths en nuestro tsconfig.json a través de la definición de baseUrl [1]. Continuemos con el ejemplo descrito anteriormente y agregaremos un par de alias para nuestros modules y services en el archivo de configuración de typescript tsconfig.json.

{
  "compilerOptions": {
    [...]
    "outDir": "./dist",
    "baseUrl": "./src",
    "paths": {
      "@modules/*": [ "modules/*" ],
      "@services/*": [ "services/*" ]
    }
} 

A partir de ahora, con este par de entradas, podremos resolver nuestros módulos de la siguiente manera:

import * as Config from '@services/config';

Si contamos con un editor de texto como VSCode o Emacs con soporte para Typescript la carga de los módulos también se resolverá de forma automática.

Desafortundamente si intentamos ejecutar inmediatamente la aplicación haciendo uso de ts-node obtendremos un error indicando que el módulo @services/config no puede ser encontrado.

$ ts-node ./src/index.ts

Error: Cannot find module '/my/path/dist/services/config'
Require stack:
...

Aún quedan un par de cosas qué hacer.

2. Aliases en Node

Para hacer uso del mismo recurso en Node.js necesitamos instalar module-alias, un paquete que permitirá resolver las rutas de la misma manera descrita anteriormente.

$ npm install -s module-alias

Ahora en el package.json añadimos:

"_moduleAliases": {
  "@modules": "dist/modules",
  "@services": "dist/services"
}

Recordemos que dist es la carpeta especificada en la entrada outDir del tsconfig.json.

3. Compilando el proyecto

Sólo resta ejecutar la compilación del código para poder hacer un uso correcto de todo lo descrito anteriormente:

$ tsc

Ahora al ejecutar

$ ts-node ./src/index.ts

El código es cargado correctamente.

La definición de servicios

Observemos también que al hacer uso de este patrón de alias para las rutas de nuestros módulos podremos realizar de forma rápida y efectiva la abstracción del registro de nuestros servicios, es decir, podemos reemplazarlos simplemente al reemplazar la ruta a la cual apunta nuestro alias. Cabe decir que preferiblemente debemos primero declarar correctamente las interfaces para no causar fallos en tiempo de ejecución.

Notas

[1] https://www.typescriptlang.org/tsconfig#paths