typeScript (y 7): decorators

los decorators ts y una reflexión

Stanley Boxer

archivado en: JavaScript / 16 Septiembre, 2016 / taller:

Los decorators son una de las característica más interesantes de typeScript, de hecho, hay quien aboga por implementarlos de fábrica en el javaScript normal, y en esencia vienen a ser una serie de metadatos adicionales que se pueden añadir a clases, métodos, propiedades y parámetros para modificar su comportamiento. Aunque no son estrictamente decorators de ts, sino una variante denominada annotations, para entender su potencial podemos pensar en los componentes de @angular, donde se usan entre otras cosas para indicar qué template está asociada con cada clase y cuál es el tag del dom asociado.

@Component({

selector: 'miComponente',

template: '<div>Bazinga!</d

iv>'

})

export class MyComponent {

...

Pero antes de seguir hablando de decorators es importante que recordemos que las propiedades de los objetos en javaScript tienen cuatro características fundamentales:

  1. value: el valor de la propiedad.
  2. writable: («escribible»). Un valor booleano que indica si se puede (true) o no (false) modificar.
  3. enumerable: si debe mostrarse (true) o no (false) en una enumeración for... in.
  4. configurable: si se puede (true) o no (false) modificar de alguna manera, por ejemplo, borrándola o cambiando los valores de las demás propiedades descriptoras.

Estas características se conocen como «propiedades descriptoras» o «propiedades atributo» y tienen una serie de valores por defecto; por ejemplo, si no se indica lo contrario, value es undefined. Estos valores se pueden precisar cuando se crea un objeto con Object.create() mediante el método Object.defineProperty(). Por ejemplo:

let libro = Object.create(null);

Object.defineProperty(

libro, // objeto

'titulo', // nombre de la propiedad

// propiedades descriptoras:

{

value: 'La Ilíada',

writable: true,

enumerable: true,

configurable: true

}

);

Volvamos ahora con los decorators de typeScript.

Decorators en métodos

Como decía, los decorators se pueden aplicar a clases, métodos, propiedades y parámetros. Comienzo con los métodos, que son los más complejos.

No voy a entrar en lo que sucede por debajo, es decir, en qué se transforma un @decorador cuando compilamos, que es un tema arduo y extenso, pues nos basta con saber en esta introducción que, en el caso de los métodos, equivale a invocar una función que recibe tres parámetros:

  1. target: la clase a la que pertenece el método decorado (lo tipamos como Object).
  2. key: una cadena con el nombre del método decorado.
  3. descriptor: las propiedades descriptoras del método decorado.

Así, por ejemplo, podríamos preparar un decorator que nos fuera trazando los métodos de una aplicación (se me hace raro escribir código en castellano, pero así creo que es más claro: ¡no hagan esto en casa sin la supervisión de un adulto!).

function trazar(target: Object, key: string, descriptor: any) {

console.log('Clase: ', target.constructor.name);

console.log('Método: ', key);

console.log('Características del método: ', descriptor);

}

De esta manera, aplicado a una clase como esta...

class Ejercicio {

respuesta: string;

@trazar

responder(respuesta:string) {

this.respuesta = respuesta;

}

getRespuesta(): string {

return this.respuesta;

}

}

let ejercicio = new Ejercicio();

ejercicio.responder('jump!');

...por consola tendríamos este resultado:

dec-met

Y no solo podemos leer un método, también podemos modificarlo y para eso hay que retornar algo, pero -dado que estamos modificando un método- ese algo debería ser una función, por ejemplo el propio descriptor con algún cambio o un equivalente. Por ejemplo, así, cambiaríamos la propiedad writable del método:

function trazar(target: Object, key: string, descriptor: any) {

descriptor.writable = true;

return descriptor;

}

Para modificar el value, que a priori es más interesante que las propiedades, es algo más complicado, ya que también en este caso debemos retornar una función (y ojo, no hay que usar aquí las arrows ()=>, ya que perderíamos la referencia del this de la clase). Por ejemplo, de esta manera modificaríamos el método responder para que la propiedad respuesta siempre fuera «Bazinga!» independientemente de lo que se le pase por parámetro.

function trazar(target: Object, key: string, descriptor: any) {

descriptor.value = function() {

this.respuesta = 'Bazinga!';

};

return descriptor;

}

Bazingar las propiedades quizás no sea muy útil, pero si recordamos que con ecmaScript 6 podemos recuperar todos los parámetros que le llegan a una función con la notación de tres puntos, podemos hacer cosas más interesantes, como incluir en nuestra traza los argumentos que llegan, tal y como explica OweR ReLoaDeD en wolksoftware.com.

function trazar(target: Object, key: string, descriptor: any) {

/* Cacheamos el método original para no perderlo */

let descriptorOriginal = descriptor.value;

descriptor.value = function(...args: any[]) {

/* Aquí recuperamos el método original */

let result = descriptorOriginal.apply(this, args);

/* Trazamos lo que queramos */

console.log('Clase: ', target.constructor.name);

console.log('Método: ', key);

console.log('Características del método: ', descriptor);

console.log('Argumentos', args);

/* Devolvemos el método original */

return result;

};

return descriptor;

}

¿Está chulo, no?

Otros decorators

En las clases los decorators funcionan de manera parecida, pero, lógicamente, solo reciben un parámetro, la clase sobre la que están actuando, que tiparemos comoFunction o any.

function decoradorClase(target:Function) {

}

@decoradorClase

class miClase {

}

Bueno, en realidad, hay una fórmula que permite pasarle parámetros al decorador, tal y como se hace en los decorators de @angular, pero en este caso hay que devolver una función con la clase. Algo así:

function decoradorClase(param1: string, param2: string) {

let clase = function (target:any) {

}

// hacemos algo...

return clase;

}

@decoradorClase('foo', 'bar')

class miClase {

}

En las propiedades el mecanismo es igual de sencillo. En este caso, el decorador recibe dos parámetros: la clase en la que se encuentra, que tiparemos como Object, y la propiedad, que tiparemos como string o symbol.

function decoradorPropiedad(target: Object, key: string) {

/* Podemos acceder al valor como si fuera una clave en un for in */

console.log(this[key]);

}

Por último, indicar que el decorador recibe tres argumentos en los parámetros: el target, el método y el índice.

function decoradorParametro(target: Object, key: string, index: number) {

}

class miClase {

trololo(@decoradorParametro miParametro:string) {}

}

}

 

Bueno, se podría contar una montonera de cosas más sobre los decoradores, pero baste con lo expuesto como introducción. Y como esta es la última entrada, creo, que le dedicaré a typeScript, termino con una reflexión.

Epílogo

El desarrollo front se caracteriza por ser un ávido glotón de novedades. Basta con que una tecnología asome la cabeza para que una legión de early adopters comience a vociferar a diestro y siniestro que lo-que-sea-que-había-dos-horas-antes ha muerto y ahora lo único razonable es trabajar con ese algo. Sin embargo, cada vez advierto más voces de gente muy seria advirtiendo que este frenesí no lleva a ninguna parte y más de uno está regresando al javaScript nativo con apoyo de librerías puntuales, como polymer. Y la verdad es que para determinados escenarios me parece la opción más razonable.

Me explico. Creo que un titanoframework, como angular, tiene dos grandes ventajas:

  • Facilita el trabajo, pues determinados problemas como el router de páginas ya está resuelto.
  • Define una metodología de trabajo que ayuda a paliar el factor autobús (solo hay uno que entiende el proyecto y le atropella un autobús).

Pero también tiene varios inconvenientes, de los que destaco tres:

  • Corren el riesgo de quedarse deprecated en poco tiempo.
  • Su curva de aprendizaje puede ser alta y luego es una trabajera encontrar profesionales que lo manejen bien.
  • Pueden ser incompatibles con otras herramientas muy útiles.

En este frenesí que caracteriza a javaScript, quizás porque apenas ahora está saliendo de la adolescencia, aunque algunos ya programasen con angular cuando solo existían tarjetas perforadas, hay dos asideros importantes para no descalabrarse:

  • El js vanilla carece de fecha de caducidad, al menos a medio plazo.
  • Cuanto más pequeña y específica sea una herramienta, menor será el impacto que tendrá su actualización. No es lo mismo, por ejemplo, pasarse del grunt al gulp o al brocoli o cómo se llame el lanzador de tareas de turno, que pasar miles de líneas de código del typeScript al javaScript.

TypeScript está muy bien, es fácil de aprender e incluso resulta instructivo si no se conocían antes clases, namespaces, módulos y demás características de los lenguajes orientados a objetos que no existían en javaScript. Y es fundamental conocerlo si se va a trabajar en angular 2 y, por extensión, en ionic 2, peeeeeeeeeero, ni @angular es la panacea universal ni typeScript aporta nada realmente sustancial a javaScript, sobre todo si lo comparamos con la versión 6.... al menos en algunos escenarios, que es donde quería llegar.

Si una aplicación es pequeña, con apenas una decena de vistas (pantallas), sin grandes desarrollos rest, que se pica sin problemas entre un par de desarrolladores, no existe razón alguna para usar un mastodonte como @angular y, por lo tanto, para usar typeScript.

|| Tags: ,

valoración de los lectores sobre typeScript (y 7): decorators

  • 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!

Aportar un comentario

*