¿Por qué podría necesitar el nombre de la clase en tiempo de ejecución?
\n\nImagine: está escribiendo un sistema complejo en TypeScript. Tiene una jerarquía de clases — User, Admin, Moderator. En algún lugar profundo del código, recibe un objeto, pero no sabe quién es realmente. Solo ve object en la consola.
O está construyendo un registrador (logger) que debe mostrar no solo [Object], sino un nombre de clase significativo: [User: John]. O está escribiendo un decorador que debe decidir cómo procesar un objeto, dependiendo de su tipo.
Aquí es donde entra en juego la pregunta con miles de visitas en Stack Overflow: "Get an object's class name at runtime". Hoy no solo daremos una respuesta, sino que miraremos debajo del capó de TypeScript y JavaScript para entender cómo funciona realmente.
\n\nSí, TypeScript es una extensión de JavaScript. Toda la magia de los tipos desaparece después de la compilación. Pero el nombre de la clase no es un tipo, es una propiedad del prototipo. Y se queda con nosotros en tiempo de ejecución.
\n\nLa forma más sencilla: constructor.name
\n\nSi alguna vez ha buscado en Google esta pregunta, seguramente ha visto esta línea única:
\n\nclass User {\n constructor(public name: string) {}\n}\n\nconst user = new User("Alice");\nconsole.log(user.constructor.name); // "User"\n\n¿Funciona? Sí. ¿Rápido? Sí. Pero hay matices que pueden morderle si no sabe cómo está organizado.
\n\n¿Cómo funciona esto bajo el capó?
\n\nCada objeto en JavaScript tiene una propiedad constructor. Esta es una referencia a la función que creó este objeto. Y cualquier función (una clase también es una función) tiene una propiedad name.
Cuando escribe user.constructor.name, el motor hace lo siguiente:
- \n
- Recorre la cadena de prototipos del objeto
user. \n - Encuentra la propiedad
constructorenUser.prototype. \n - Lee el
namede la función constructoraUser. \n
Es una operación económica, pero no es gratuita. En la mayoría de los casos, ni siquiera lo notará.
\n\n¿Cuándo fallará constructor.name?
\n\nAquí hay un problema clásico:
\n\nconst obj = Object.create(null);\nconsole.log(obj.constructor); // undefined\n// console.log(obj.constructor.name); // TypeError!\n\nUn objeto creado con Object.create(null) no tiene prototipo. No tiene constructor. Tenga cuidado.
El segundo caso es la minificación. Si usa un empaquetador (Webpack, Terser) que renombra las clases, constructor.name podría devolver algo como t o e. Esto es solo un problema para producción, pero vale la pena saberlo.
Alternativa: instanceof y comprobaciones personalizadas
\n\nA veces no necesita solo el nombre, sino una verificación: "¿Es este objeto una instancia de la clase X?"
\n\nclass Animal {}\nclass Dog extends Animal {}\n\nconst dog = new Dog();\n\nconsole.log(dog instanceof Dog); // true\nconsole.log(dog instanceof Animal); // true\nconsole.log(dog instanceof Object); // true\n\ninstanceof funciona a través de la cadena de prototipos. Verifica si Dog.prototype está en la cadena de prototipos del objeto dog. Si es así — true.
Pero instanceof no le da una cadena con el nombre. Da un valor booleano. Para registros o serialización, esto no es adecuado.
Combinando: verificación + nombre
\n\nPuede escribir una utilidad que primero verifique el tipo y luego devuelva el nombre:
\n\nfunction getClassName(obj: any): string | null {\n if (obj === null || obj === undefined) return null;\n if (typeof obj !== 'object' && typeof obj !== 'function') return null;\n\n // Protección contra Object.create(null)\n if (!obj.constructor) return 'Object (sin prototipo)';\n\n const name