typeScript (5): módulos

Un primer vistazo a los módulos en typeScript

Stanley Boxer

archivado en: JavaScript / 6 septiembre, 2016 / taller:

Con lo que hemos visto hasta el momento de typeScript, espero que hasta los más refractarios se hayan dado cuenta de sus bondades, pero por si a alguien aún le quedaba alguna duda, aquí van los módulos, que en javaScript no existían sin librerías especializadas hasta su versión 6, aunque sí en node.

En general, un módulo sirve para encapsular código, es decir, para controlar qué se expone públicamente y qué se mantiene resguardado. Al igual que las piezas de un automóvil solo se relacionan unas con otras en determinados puntos y por lo demás son independientes, los módulos actúan como cajas negras permitiendo distribuir la lógica de una aplicación en partes estancas, reutilizables y articulables.

El mecanismo básico de los módulos en typeScript es similar al que se usa en node. Dentro de un archivo, solo aquello que se exporta es accesible desde fuera (aunque sí desde dentro) y para usarlo en otro archivo hay que importarlo. Por ejemplo, si de este módulo solo podemos atacar directamente al método foo de myObject, mientras que bar es inaccesible desde cualquier otro lado que no sea el propio módulo.

foo.ts

function bar() {

console.log('bar');

}

const myModule = {

foo: function() {

console.log('foo');

}

};

export { myModule };

*

app.ts

import { myModule } from './foo';

// esto funciona

myModule.foo();

// esto casca

bar();

Se puede exportar cualquier cosa que viva dentro de un archivo, desde una variable miserable, hasta un interfaz, una clase, una función 0 un objeto. Y podemos exportar un elemento, varios o todos, en este último caso, usando la expresión export * from 'nombreModulo'.

interface Foo {

bazinga: string;

}

class Bazinga {

bar: string;

constructor(bar) {

this.bar = bar;

}

}

/* O bien exportamos los elementos uno a uno */

export { Foo };

export { Bazinga };

/* O bien exportamos todo lo que hay en el módulo */

export * from './foo';

Además, podemos exportar los elementos usando un alias con el término as (como).

export { Bazinga as Awesome };

Por lo menos en el momento de escribir estas líneas, jugar con los alias puede dar problemas con algunas librerías de carga de módulos.

La jugada es parecida para importar. Como hemos visto, la fórmula más sencilla es indicar qué se importa desde lo que hay en la ruta indicada.

import { Awesome } from './foo';

Pero también en este caso podemos usar comodines, para lo cual hay que cachear el contenido en un alias que luego usaremos para atacar a los elementos del módulo importado .

import * as Bar from './foo';

let instFoo = new Bar.Foo();

instFoo.meth();

Además, existe una sintaxis alternativa para definir la exportación / importación que quizás le resulte más amable a quien esté acostumbrado a trabajar con node. En lugar de referirse a un objeto, con sus llaves, se usa el signo igual (=), pero en este caso solo puede exportarse una cosa. Por ejemplo:

/* foo.ts */

 

let ukaLele = 'trololo';

class Foo {

meth() {

console.log('foo');

}

}

export = Foo;

/* app.ts */

import foo = require('./foo');

Sin embargo, esta sintaxis solo funciona cuando usamos el modo commonjs o amd para los módulos, que es el tema que trataré a continuación.

Introducción a system.js

Como ya sabemos, typeScript solo sirve en desarrollo, cuando estamos escribiendo código, pues luego se transpila, se traduce, en javaScript normal. Según el formato en que estemos volcando el código final traducirá el sistema de módulos de una manera u otra. Si es para ecma6, usará el sistema de módulos nativo de js; si es para ecma5, utilizará common js, pero podemos modificar estos valores por defecto en las compilerOptions del tsconfig.json. La clave a definir se denomina module y los valores que puede tener son:

  • 'none'
  • 'commonjs'
  • 'amd' (el de require)
  • 'system'
  • 'umd'
  • 'es6'
  • 'es2015'

Según el modo escogido, tendremos o no que añadir al proyecto alguna librería complementaria. No voy a entrar a analizar cada caso, a la documentación oficial me remito, pues sería un tostón, pero sí me interesa tratar con cierto detalle el modo system por tres razones: es compatible con ecma5, permite que ts concatene todos los archivos en uno solo final (indicando true en el parámetro outFile del tsconfig.json) y, sobre todo, porque es el que se utiliza en la familia 2.* de angular (en realidad, se maneja system, pero con otro tipo de módulos).

Entonces, para entender cómo funciona, descargamos la librería systemjs con npm (o jspm, que para este ejemplo da igual) desde el directorio donde tengamos nuestro proyecto ts.

npm install systemjs

A continuación, copiamos el contenido que hay en dist y nos lo llevamos a un directorio más razonable que node_modules/systemjs... por ejemplo, a vendor y enlazamos la librería en el index.html.

<script src="vendor/systemjs/system.js"></script>

Con todo listo, ya solo nos falta escribir el código js que necesita system, el cual pondremos en un par de tags scripts al final del <body>, y ya podemos entrar en faena. Pero para que se entienda lo que hacemos, vamos a preparar dos módulos muy sencillos. En uno, que podemos llamar foo, declaramos y exportamos una clase.

/* foo.ts */

class Foo {

meth() {

console.log('foo');

}

}

export {Foo};

Y en otro, que podemos llamar app, importamos el módulo foo.

import { Foo } from './foo';

let instFoo = new Foo ();

instFoo.meth();

Ahora ya sí volvemos al index.html, al script, donde en un caso real tendríamos que definir dos partes: por un lado, la configuración, que en nuestro caso podemos dejar en blanco; y, por otro, indicamos cuál es el primer archivo que debe cargar. Además, añadimos un flag para que añada solo las extensiones .js. Algo así.

<script>

/* Aquí iría la configuración */

System.config({

});

/* Indicamos que añada la terminación .js a todos los archivos */

System.defaultJSExtensions = true;

/* Cargamos el archivo principal (que funciona como una promesa */

SystemJS.import('./app')

.then(null, console.error.bind(console));

</script>

Como system js carga los archivos mediante ajax, para que funcione debemos levantar un servidor, ya sea este un apache, un http-server, un lite-server o el que sea, pues de lo contrario nos dará un problema de cors.

Ale op, pues con esto ya tendríamos nuestros módulos operativos, pero, ojo, en este caso ha sido así de sencillo porque los archivos ya están compilados, traducidos, por typeScript al código js -un tanto turbio- que necesita system; pero si estuviéramos  trabajando directamente contra los archivos ts, sin transpilar, necesitaríamos añadir plugins y definir un par de parámetros en System.config. Y si el proyecto es de @angular, el asunto pasa a ser mucho más complejo. Sin embargo, como ya trataré esto con más detalle cuando hable de este framework, que será en breve, y con esto ya podemos trastear con los módulos, de momento lo dejo aquí.

|| Tags: , , ,

valoración de los lectores sobre typeScript (5): módulos

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración negativa
  • 4 sobre 5 (1 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Los comentarios están cerrados.