ProgrammationDéveloppeur TypeScript

Comment implémenter et typifier correctement la surcharge (overload) de fonctions dans TypeScript au niveau des signatures ? Pourquoi ne peut-on pas utiliser plusieurs corps de fonction, et que se passe-t-il avec les types des paramètres à l'intérieur du corps de la fonction ? Donnez des scénarios pratiques d'utilisation et les erreurs les plus fréquentes.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

La surcharge des fonctions permet de créer une seule fonction avec différentes variantes de paramètres acceptés et de valeurs de retour. Dans d'autres langages, comme C# ou Java, on peut décrire plusieurs fonctions avec le même nom, mais avec des types ou un nombre d'arguments différents. TypeScript rapproche ce mécanisme par la surcharge des signatures au niveau des types, mais le corps de la fonction reste toujours le même.

L'histoire de la question — en JavaScript, il n'y a pas de support natif pour la surcharge par type ou quantité de paramètres. Avec l'apparition de TypeScript, la surcharge est simulée en déclarant plusieurs signatures de fonction consécutivement et une seule implémentation, à l'intérieur de laquelle on différencie manuellement quel type on utilise actuellement.

Le problème survient si on ne fait pas attention à la correspondance avec les signatures déclarées : aux paramètres à l'intérieur du corps de la fonction, TypeScript attribue le type le plus général (union de tous les types de paramètres) et le programmeur doit faire des vérifications et des type guards.

Solution : Typifier strictement les surcharges dans des signatures explicites, travailler correctement avec les types d'union et les type guards, documenter correctement le comportement.

Exemple de code :

function format(value: string): string; function format(value: number, locale: string): string; function format(value: any, locale?: string): string { if (typeof value === 'number') { return value.toLocaleString(locale); } return value.trim(); } format(' hello '); // 'hello' format(123456, 'ru-RU'); // '123 456'

Caractéristiques clés :

  • Plusieurs signatures de surcharge consécutives, une réalisation en dessous.
  • Pour travailler avec les paramètres dans le corps, on utilise soit un type d'union, soit any/unknown.
  • Le type du corps lui-même est plus large/universel que les signatures au-dessus.

Questions pièges.

Peut-on déclarer plusieurs fonctions avec le même nom et des corps différents, comme en C# ?

Non. Dans TypeScript (et JavaScript), il n'existe en réalité qu'une seule fonction avec ce nom. Les surcharges fonctionnent uniquement au niveau des types pour le compilateur, mais il n'y a qu'un seul corps de fonction.

Que se passe-t-il si on ne réalise pas la signature avec les paramètres les plus généraux après les signatures de surcharge ?

TypeScript émettra une erreur de compilation. Il doit toujours y avoir une fonction-implémentation, dont les paramètres couvrent tous les possibles types de surcharge.

function foo(a: string): string; function foo(a: number): number; // pas de corps — erreur

Puis-je à l'intérieur du corps utiliser des propriétés spécifiques à une seule signature particulière ?

Non, uniquement après une vérification de type (type guard) ou un type casting. Sinon, TypeScript ne permettra pas d'utiliser directement ces propriétés, car il n'est pas clair, selon quelle signature l'appel se fait actuellement.

function bar(x: string): number; function bar(x: number): number; function bar(x: string | number): number { if (typeof x === 'string') return x.length; return x * 2; }

Erreurs typiques et anti-patterns

  • N'ajoutent pas d'implémentation après les signatures de surcharge — la compilation échouera.
  • Dans le corps de la fonction, utilisent des propriétés spécifiques sans vérification de type.
  • Compliquent trop les surcharges, rendant le code illisible.
  • Ne décrivent pas explicitement les types de retour ou oublient de mettre à jour les signatures lors de changements de logique de fonction.

Exemple de la vie réelle

Cas négatif

Oubli d'ajouter une signature-implémentation universelle :

function sum(a: string, b: string): string; function sum(a: number, b: number): number; // pas d'implémentation — le compilateur se plaint

Avantages :

  • Idée de décrire une API pratique.

Inconvénients :

  • Le code ne compile pas, impossible de traiter aucune variante.

Cas positif

Implémentent la surcharge conformément à la norme :

function sum(a: string, b: string): string; function sum(a: number, b: number): number; function sum(a: any, b: any): any { return typeof a === 'string' && typeof b === 'string' ? a + b : a + b; }

Avantages :

  • Tous les cas sont traités correctement, les types sont inférés au moment de la compilation.

Inconvénients :

  • Le corps de la fonction nécessite un contrôle manuel des types, il ne faut pas oublier de vérifier chaque variante.