Con esto de Angular, me he puesto a escribir cosas de TypeScript. En concreto, sobre Decoradores de TypeScript. Y ya puestos, pues con ChatGPT le voy pidiendo ejemplos de código y explicaciones, para llevarlos a mis pruebas TypeScript en github.
Pues bien, todo más o menos «sobre ruedas», por decir algo. ChatGPT ayuda bastante y soy más rápido haciendo mis ejemplos y luego redactando en la wiki.
Pero no es oro todo lo que reluce. Hay varios tipos de decoradores, de clase, de atributo de clase, de método y de parámetro de método.
Quise hacer a modo de prueba un decorador de forma que cuando alguien haga un new de la clase decorada, saque un log de que se ha hecho el new. Buscando por google y el ejemplo de ChatGPT te lo ponen siempre más o menos igual: una función decoradora que saca el log, una clase decorada y se hace un new para ver que sale.
Pero ni la mayoría de los ejemplos/tutoriales de google ni ChatGPT te dicen dos cositas:
- Al decorador se le llama una sola vez y es cuando se declara la clase. No cuando se instancia. En los ejemplos hacen un solo new y sale el log del decorador, todo aparentemente «guay». Pero si no haces ningún new también sale. El log del decorador sale independientemente de si haces new o no haces new y si haces un new o quince news. Sale una sola vez. Siempre.
- La sintaxis que te ponen es experimental, así que ni siquiera compila. Tienes que habilitar la opción experimentalDecorators.
Así que se sigue investigando y se descubre el truco. Para nuestro ejemplo, el decorador debe devolver una clase hija de la original, con un constructor nuevo que saque el log que queremos por pantalla y llame al constructor de la clase padre original.
Y algo parecido pasa con los decoradores de método. Debes devolver una función alternativa que haga lo que tú quieres (el log en nuestro ejemplo) y llamar a la función original.
Y con los atributos, un poco más raro. Debes definir un método set() y get() para el atributo que haga lo que tu quieres y asignarlo al atributo en un PropertyDescriptor. Debes definir los dos, aunque sólo quieras hacer algo en el set() o en el get()
Y la liamos con los decoradores de parámetro de método. Le pido a ChatGPT el código para hacer log cada vez que llamen a un método que tenga un parámetro decorado, me lo hace. Y no funciona. Miro ejemplos por internet y todos se quedan en lo básico, sacar un log cuando llaman al decorador la primera y única vez que lo llaman. Media hora de charla con ChatGPT para intentar arreglar el código, no hay manera.
Así que me fuí a la documentación oficial: Parameter Decorators y veo un ejemplo rarísimo, además mezclado con un decorador de método. Y mirando con más calma, veo este aviso
NOTE A parameter decorator can only be used to observe that a parameter has been declared on a method.
¡Aclarado!. El decorador de parámetro básicamente no sirve para nada :). Sólo te avisa que un parámetro ha sido decorado, pero no hay forma de que te avise cada vez que se llame a un método que tenga el parámetro decorador para hacer algo como sacar log o cambiar algo. De ahí que tengas que mezclarlo con un decorador de método:
- El código que tienes que hacer en el decorador de parámetro es guaradarse en algún sito que el parámetro ha sido decorado con tal decoración. Una tabla o mapa donde pongas nombre del parámetro, de qué clase y método y qué decoración tiene.
- Y luego tienes que hacer un decorador del método que devuelva un método alternativo que consulte eso que te has guardado para hacerle la lógica al parámetro.
Y ChatGPT, conmigo, emperrado en arreglar un código que no funciona. Y básicamente porque los montones de ejemplos que hay por internet tampoco han profundizado un poco en el tema, se quedan en que me avisa (una sola vez) cuando decoro el parámetro, independientemente de que lo use o no.