Reflexión e introspección en TypeScript: cómo el código se mira a sí mismo
\n\nImagina un programa que es capaz de analizarse a sí mismo en tiempo de ejecución. Puede saber qué tipos tienen sus variables, qué propiedades tienen los objetos e incluso tomar decisiones basadas en esa información. ¿Suena a magia? En realidad, esto es reflexión e introspección — potentes técnicas de metaprogramación que también están disponibles en TypeScript.
\n\nEn este artículo, entenderemos en qué se diferencia la reflexión de la introspección, cómo usarlas en TypeScript y por qué pueden ser útiles en proyectos reales. Sin teoría aburrida — solo código vivo y ejemplos prácticos.
\n\n¿Qué son la introspección y la reflexión?
\n\nAclaremos conceptos de inmediato. En programación, estos términos a menudo se confunden, pero hay una diferencia:
\n\n- \n
- Introspección — es la capacidad de un programa de observar su propia estructura. Puedes saber qué propiedades tiene un objeto, qué tipo tiene, pero no puedes modificarlas. \n
- Reflexión — es un nivel más avanzado: el programa no solo se observa a sí mismo, sino que también modifica su comportamiento en tiempo de ejecución. Por ejemplo, invoca métodos dinámicamente o crea nuevas instancias de clases. \n
En JavaScript, sobre el que se basa TypeScript, la reflexión se implementa mediante Reflect y Proxy. Pero TypeScript añade su propia capa — el tipado estático, que desaparece parcialmente durante la compilación. Y aquí es donde comienza lo interesante.
Introspección de tipos: type guards y typeof
\n\nLa forma más sencilla de mirar dentro del código es usar type guards. Son construcciones que verifican el tipo de dato en tiempo de ejecución:
\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string';\n}\n\nfunction processValue(value: unknown) {\n if (isString(value)) {\n // TypeScript sabe que value es un string\n console.log(value.toUpperCase());\n } else {\n console.log('Esto no es un string');\n }\n}\n\nObserva value is string — esto es un predicado de tipo. Le indica a TypeScript que, después de la verificación, la variable pertenece a un tipo específico. Esto es introspección pura: observamos el tipo, pero no lo modificamos.
Reflexión a través de la API Reflect
\n\nEl objeto Reflect es un mecanismo de reflexión integrado en JavaScript. Permite realizar acciones que antes se hacían con métodos "mágicos", pero ahora están unificadas. Aquí hay algunos ejemplos:
const user = {\n name: 'Alexey',\n age: 30,\n};\n\n// Introspección — obtenemos la lista de claves\nconsole.log(Reflect.ownKeys(user)); // ['name', 'age']\n\n// Reflexión — leemos una propiedad dinámicamente\nconst propertyName = 'name';\nconsole.log(Reflect.get(user, propertyName)); // 'Alexey'\n\n// Reflexión — establecemos una propiedad dinámicamente\nReflect.set(user, 'name', 'María');\nconsole.log(user.name); // 'María'\n\n¿Por qué Reflect es mejor que el clásico user[prop]? Primero, devuelve un boolean para las operaciones de asignación, lo cual es útil para verificaciones. Segundo, funciona correctamente con proxies y descriptores de propiedades. Para TypeScript esto es especialmente importante, ya que la reflexión permite sortear el tipado estático cuando realmente es necesario.
Decoradores: reflexión potenciada
\n\nLos decoradores son, quizás, el ejemplo más claro de reflexión en TypeScript. Permiten anotar clases, métodos, propiedades y parámetros, y luego modificar su comportamiento. Desde la versión TypeScript 5.0, los decoradores se volvieron estándar (stage 3).
\n\nAsí se ve un decorador simple para registrar la llamada de un método:
\n\nfunction logMethod(\n target: any,\n propertyKey: string,\n descriptor: PropertyDescriptor\n) {\n const originalMethod = descriptor.valu