Reflexión e introspección en TypeScript: cómo el código se mira a sí mismo

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Reflexión e introspección en TypeScript: cómo el código se mira a sí mismo

\n\n

Imagina 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\n

En 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\n

Aclaremos 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
\n\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.

\n\n

Introspección de tipos: type guards y typeof

\n\n

La 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\n
function 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\n

Observa 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.

\n\n

Reflexión a través de la API Reflect

\n\n

El 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:

\n\n
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.

\n\n

Decoradores: reflexión potenciada

\n\n

Los 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\n

Así se ve un decorador simple para registrar la llamada de un método:

\n\n
function logMethod(\n  target: any,\n  propertyKey: string,\n  descriptor: PropertyDescriptor\n) {\n  const originalMethod = descriptor.valu                

Blogs

Book Recommendations