0

TypeScript | Javascript – 2nd Edition #6

En este sexto post tocaremos los temas: namespaces, módulos y decoradores, como se usan en TypeScript, que no brinda para trabajar con ellos y que beneficios tienen. Nuevamente utilizaremos VS Code en algunos ejemplos.

Namespaces

Muchos lenguajes de programación como C#, Java y TypeScript nos brindan una forma de organizar nuestro código por medio de Namespaces. Usaremos estos namespaces para agrupar funcionalidades similares.

Supongamos que tenemos varias clases validadoras como en el ejemplo:

interface StringValidator {
	isAcceptable(s: string): boolean;
}

let lettersRegexp = /^[A-Za-z]+$/;
let numberRegexp = /^[0-9]+$/;

class LettersOnlyValidator implements StringValidator {
	isAcceptable(s: string) {
    	return lettersRegexp.test(s);
	}
}

class ZipCodeValidator implements StringValidator {
	isAcceptable(s: string) {
    	return s.length === 5 && numberRegexp.test(s);
	}
}

A medida que nuestra aplicación crezca tal vez sea necesario agregar más validadores. Es una buena práctica tener nuestros validadores organizados en un Namespace. Por ahora, no le prestemos atención a la palabra export lo veremos más adelante.

namespace Validation {
	export interface StringValidator {
    	    isAcceptable(s: string): boolean;
	}

	const lettersRegexp = /^[A-Za-z]+$/;
	const numberRegexp = /^[0-9]+$/;

	export class LettersOnlyValidator implements StringValidator {
       	isAcceptable(s: string) {
        	return lettersRegexp.test(s);
      	}
	}

	export class ZipCodeValidator implements StringValidator {
    	isAcceptable(s: string) {
        	return s.length === 5 && numberRegexp.test(s);
    	}
	}
}

Supongamos que no queremos hacer que todas nuestras validaciones estén en el mismo archivo. TypeScript nos permite separarlos y hacer referencia al archivo donde se encuentra el namespace base 

Validation.ts

namespace Validation {
	export interface StringValidator {
    	isAcceptable(s: string): boolean;
	}
}

LettersOnlyValidator.ts

/// <reference path="Validation.ts" />
namespace Validation {
	const lettersRegexp = /^[A-Za-z]+$/;
	export class LettersOnlyValidator implements StringValidator {
    	isAcceptable(s: string) {
        	return lettersRegexp.test(s);
    	}
	}
}

ZipCodeValidator.ts

/// <reference path="Validation.ts" />
namespace Validation {
	const numberRegexp = /^[0-9]+$/;
	export class ZipCodeValidator implements StringValidator {
    	isAcceptable(s: string) {
        	return s.length === 5 &amp;&amp; numberRegexp.test(s);
    	}
	}
}

TestExamaple.ts

/// <reference path="Validation.ts" />
/// <reference path="LettersOnlyValidator.ts" />
/// <reference path="ZipCodeValidator.ts" />

let strings = ["Hello", "98052", "101"];

let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();

for (let s of strings) {
	for (let name in validators) {
    	console.log(""" + s + "" " + (validators[name].isAcceptable(s) ? " matches " : " does not match ") + name);
	}
}

Módulos

Cada módulo tiene su propio ámbito, esto significa que todo su contenido, clases, variables, funciones, etc declaradas dentro no serán visibles al menos que se exporten. Es aquí la importancia de la palabra export, export dará visibilidad a las clases que quieran usarla  y para ser usados deben ser importados.

Los módulos pueden importarse entre sí por medio de un cargador de módulos. En nuestro caso estamos usando CommonJS pero podemos utilizar cualquier otro. Veamos un ejemplo supongamos que tenemos una clase que era un módulo:

customer.ts

class Customer{

    let name: string;
    GetName(){
        return name;
    }
}

export { Customer };

Simplemente por tener la palabra export ya se convertirá en un módulo. Export está diciendo que Customar podrá ser consumida o utilizada fuera del ámbito del propio módulo.

Para no exponer el nombre original podemos exportarlo como un alias de la siguiente manera:

export { Customer as PrincipalCustomer };

Ahora para utilizar en nuestra app el módulo Customer debemos importarlo de esta manera:

import{ Customer} from "./customer";

let myCustomer = new Customer();

Muchos pensaran “le fatla e .ts”. No es necesario ya que por default sabrá que se refiere a ese tipo de extensión. Supongamos que nuestro módulo posee más de una clase, y queremos importarlo en una variable:

import * as list from "./customer";

let myCustomer = new list.Customer();

Una buena opción es exportarlo por default, cada módulo puede tener una y solo una exportación por default.

customer.ts

class Customer{
    ...
}

export default Customer;

app.ts

import Customer from "./customer";
let myCustomer = new Customer();

Realmente el uso de módulos es bastante sencillo. El tema es más extenso pero con esto nos alcanza para tener un excelente punto de partida.

Decoradores

Los decoradores vienen de Angular (2) y fueron solicitados por el equipo de Google. Si, aunque no lo crean, el equipo de Microsoft y el equipo de Google trabajaron juntos para poder implementarlo en TypeScript.

Antes que nada, para poder hacer uso de los decorator, debemos cambiar una configuración en nuestro tsconfig y agregar lo siguiente.

{
  "compilerOptions": {
	"target": "es5",  'ESNEXT'. */
	"module": "commonjs",
     "experimentalDecorators": true
  }
}

Aunque suene raro que diga experimental, en muchos lugares ya se usa en producción así que no debemos preocuparnos por esto.

Un decorador es un tipo especial de declaración que adjunta metadata o añadir una cierta característica que no debería cambiar el comportamiento a una clase, método, propiedades o parámetros.

Para hacer un decorador debemos cumplir algunas especificaciones dependiendo de a cual queramos afectar.

type ClassDecorator = <TFunction extends Function>(target: TFunction: TFunction | void;

type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPrpoertyDescriptor<T>): void;

Type PropertyDecorator = (target: Object, propertyKey: string | symbol): void;

Type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number): void;

Para mejorar su funcionamiento vamos a hacer un ejemplo donde tendremos una clase log que irá registrando el comportamiento de una clase.

function logClass(constructor: Function){
	console.log(Date.now() + " - LogClass......: " + constructor["name"]);
}

function logMethod(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>):void{
	console.log(Date.now() + " - LogMethod.....: " + propertyKey.toString());
}

function logParameter(target: Object, propertyKey: string | symbol, parameterIndex: number):void{
	console.log(Date.now() + " - LogParamter...: " + propertyKey.toString());
}

Lo que podemos ver en nuestra clase es que tenemos 3 funciones: una para class, otra para un method y por último para un parámetros. Ahora veamos cómo se vería nuestra clase decorada:

@logClass
class Calculadora{

	constructor(){}

	@logMethod
	Sumar(arg1: number, @logParameter arg2: number){
    	    return arg1 + arg2;
	}

}

let calc = new Calculadora();
console.log(calc.Sumar(1,2));

El símbolo @ le dirá compilador que es un decorador.  Para ejecutar nuestro código primero debemos compilarlo con tsc y luego lo ejecutaremos con nodeJs desde la consola de VS Code.

tsc decorator.ts

node decorator.js

Conclusión

Hemos visto el uso  namespaces, módulos y decoradores. Nuevamente es simplemente una introducción y un buen punto de partida para comenzar a entrar más en profundidad. En los próximos post veremos un ejemplo de una aplicación sencilla, un abm, utilizando TypeScript junto a JQuery.

Fernando Sonego

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *