À 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.
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.
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 :
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.
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 :
Inconvénients :
Le type de constructeur a été isolé, l'usine createInstance est paramétrée via CorrectConstructable<T>, les signatures sont respectées.
Avantages :
Inconvénients :