Pourquoi pourriez-vous avoir besoin du nom de la classe en runtime ?
\n\nImaginez : vous écrivez un système complexe en TypeScript. Vous avez une hiérarchie de classes — User, Admin, Moderator. Quelque part au fond du code, vous recevez un objet, mais vous ne savez pas qui il est réellement. Vous ne voyez que object dans la console.
Ou vous construisez un logger qui doit afficher non pas simplement [Object], mais un nom de classe significatif : [User: John]. Ou vous écrivez un décorateur qui doit décider comment traiter un objet en fonction de son type.
C'est là qu'intervient la question avec des milliers de vues sur Stack Overflow : "Get an object's class name at runtime". Aujourd'hui, nous ne donnerons pas seulement une réponse, mais nous regarderons sous le capot de TypeScript et JavaScript pour comprendre comment cela fonctionne réellement.
\n\nOui, TypeScript est une surcouche de JavaScript. Toute la magie des types disparaît après la compilation. Mais le nom de la classe n'est pas un type, c'est une propriété du prototype. Et il reste avec nous en runtime.
\n\nLa méthode la plus simple : constructor.name
\n\nSi vous avez déjà cherché cette question sur Google, vous avez probablement vu cette ligne unique :
\n\nclass User {\n constructor(public name: string) {}\n}\n\nconst user = new User("Alice");\nconsole.log(user.constructor.name); // "User"\n\nÇa marche ? Oui. Rapide ? Oui. Mais il y a des nuances qui peuvent vous mordre si vous ne savez pas comment cela fonctionne.
\n\nComment cela fonctionne-t-il sous le capot ?
\n\nChaque objet en JavaScript a une propriété constructor. C'est une référence à la fonction qui a créé cet objet. Et toute fonction (une classe est aussi une fonction) a une propriété name.
Lorsque vous écrivez user.constructor.name, le moteur fait ce qui suit :
- \n
- Il parcourt la chaîne de prototypes de l'objet
user. \n - Il trouve la propriété
constructorsurUser.prototype. \n - Il lit
namede la fonction constructeurUser. \n
C'est une opération peu coûteuse, mais elle n'est pas gratuite. Dans la plupart des cas, vous ne le remarquerez même pas.
\n\nQuand constructor.name ne fonctionne-t-il pas ?
\n\nVoici un piège classique :
\n\nconst obj = Object.create(null);\nconsole.log(obj.constructor); // undefined\n// console.log(obj.constructor.name); // TypeError!\n\nUn objet créé via Object.create(null) n'a pas de prototype. Il n'a pas de constructor. Soyez prudent.
Deuxième cas : la minification. Si vous utilisez un bundler (Webpack, Terser) qui renomme les classes, constructor.name peut retourner quelque chose comme t ou e. C'est un problème uniquement pour la production, mais il faut le savoir.
Alternative : instanceof et vérifications personnalisées
\n\nParfois, vous n'avez pas besoin seulement du nom, mais d'une vérification : "Cet objet est-il une instance de la classe 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 fonctionne via la chaîne de prototypes. Il vérifie si Dog.prototype est présent dans la chaîne de prototypes de l'objet dog. Si oui — true.
Mais instanceof ne vous donne pas une chaîne avec le nom. Il donne une valeur booléenne. Pour les logs ou la sérialisation, ce n'est pas adapté.
Combinaison : vérification + nom
\n\nVous pouvez écrire un utilitaire qui vérifie d'abord le type, puis retourne le nom :
\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 // Protection contre Object.create(null)\n if (!obj.constructor) return 'Object (no prototype)';\n\n const name