ProgrammationDéveloppeur Frontend/Fullstack

Comment fonctionne la typage des constructeurs de classes en TypeScript et quelles difficultés peuvent survenir lors de l'héritage ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question

À l'origine, JavaScript n'avait pas de typage strict des classes et de leurs constructeurs, ce qui entraînait des erreurs à l'exécution. TypeScript a ajouté un système de types pour une programmation plus sécurisée et a pris en charge la typage des constructeurs avec héritage, ce qui est important pour le développement d'applications de grande taille.

Problème

La typage des constructeurs en TypeScript nécessite de prendre en compte simultanément la signature du constructeur, le type de l'instance créée et les particularités de l'héritage. Des problèmes surviennent si les signatures des constructeurs dans la classe de base et la classe dérivée ne correspondent pas, ou si seul le type de retour est typé sans tenir compte des paramètres d'entrée du constructeur.

Solution

En TypeScript, on peut explicitement typiser les constructeurs via des signatures spéciales, en utilisant l'expression new (...args: any[]) => T. Lors de l'héritage, il est important de maintenir la cohérence des signatures et d'étendre correctement les classes de base.

Exemple de code :

class Animal { constructor(public name: string) {} } class Dog extends Animal { constructor(name: string, public breed: string) { super(name); } } // Type du constructeur function createInstance<T>(C: new (...args: any[]) => T, ...args: any[]): T { return new C(...args); } const dog = createInstance(Dog, 'Rex', 'Labrador');

Caractéristiques clés :

  • La signature du constructeur est une entité séparée, déclarée via new
  • Respect de la compatibilité des paramètres entre le constructeur de base et le constructeur dérivé
  • Possibilité de création d'instances universelles via des constructeurs typés

Questions pièges.

Peut-on déclarer plusieurs constructeurs dans une classe, comme en Java ou C# ?

Non, TypeScript ne supporte pas les constructeurs multiples. Pour simuler la surcharge, on utilise des surcharges de signatures (overloads) avec une seule réalisation. Approche correcte :

class Example { constructor(x: string); constructor(x: number); constructor(x: number | string) { // Une seule réalisation } }

Peut-on typiser uniquement le type de retour du constructeur, en ignorant les paramètres ?

Non, la signature du constructeur doit obligatoirement inclure les paramètres. Exemple de typisation correcte :

interface Constructable<T> { new (...args: any[]): T; }

Que se passe-t-il si l'on déclare un constructeur dans une classe dérivée sans appeler super ?

Une erreur de compilation se produira : le constructeur de la sous-classe doit appeler super avant d'accéder à this.

Erreurs typiques et anti-modèles

  • Paramètres non compatibles du constructeur de la classe de base et de la classe dérivée
  • Absence d'appel à super dans le constructeur dérivé
  • Typage incorrect du constructeur lors de l'abstraction via des usines

Exemple réel

Cas négatif

Dans le projet, une classe de base Animal a été utilisée avec un constructeur (name), et dans l’héritier Dog, on a ajouté (name, breed), mais on a oublié d'étendre correctement la signature.

Avantages :

  • Documentent les nouveaux paramètres

Inconvénients :

  • Incompatibilité lors de la création d'instances via des usines universelles, erreurs à l'étape de compilation

Cas positif

Le type de constructeur a été isolé, l'usine createInstance est paramétrée via CorrectConstructable<T>, les signatures sont respectées.

Avantages :

  • Sécurité des types, comportement prévisible
  • Facilité à écrire des fonctions universelles

Inconvénients :

  • Nécessite une réflexion plus approfondie sur la typisation