Cours JavaScript et TypeScript · Module 6: Bases de TypeScript

La surcharge de fonctions

13 min de lecture
Dans cette leçon9

« À Jurassic Park, la précision est essentielle », dit le Dr Henry Wu en observant son équipement de laboratoire hautement spécialisé. « Le même analyseur d'ADN peut examiner des échantillons sous différents formats : du sang de dinosaure, en passant par des fossiles, jusqu'à l'ambre. De la même façon en TypeScript, une seule fonction peut gérer différents jeux de paramètres et retourner différents types de données. »

Bienvenue dans le monde de la surcharge de fonctions : un mécanisme puissant de TypeScript qui permet de définir des fonctions acceptant différents types de paramètres et retournant différents types de résultats.

Qu'est-ce que la surcharge de fonctions ?

La surcharge de fonctions est un mécanisme qui permet de définir plusieurs signatures pour une seule et même fonction. On peut ainsi créer une fonction qui se comporte différemment selon le nombre, les types et la structure des paramètres passés.

Autrement dit, tout comme les scientifiques de Jurassic Park peuvent utiliser le même équipement pour différentes procédures, on peut utiliser une seule fonction pour différentes opérations, tout en conservant une sécurité de types complète.

Les bases de la surcharge de fonctions

En TypeScript, la surcharge de fonctions se compose de deux parties :

  1. Les signatures de surcharge : les définitions des types de paramètres et de valeurs de retour
  2. La signature d'implémentation : l'implémentation réelle de la fonction, qui doit être compatible avec toutes les signatures de surcharge

Regardons un exemple simple :

1// Overload signatures
2function analyzeSample(id: number): string;
3function analyzeSample(name: string): string;
4
5// Implementation signature
6function analyzeSample(argument: number | string): string {
7  if (typeof argument === "number") {
8    return `Analyzing sample with ID: ${argument}`;
9  } else {
10    return `Analyzing sample: ${argument}`;
11  }
12}
13
14// Using the function
15const result1 = analyzeSample(42);         // TypeScript knows the argument is number
16const result2 = analyzeSample("Raptor-01"); // TypeScript knows the argument is string
17// const error = analyzeSample(true);      // Error, boolean is not an accepted type

Dans l'exemple ci-dessus :

  1. On a défini deux signatures de surcharge : l'une acceptant number, l'autre acceptant string
  2. On a créé une seule implémentation qui gère les deux types
  3. TypeScript s'assure que la fonction n'est appelée qu'avec des arguments des bons types

La surcharge avec différents types de retour

Toute la puissance de la surcharge se révèle lorsqu'une fonction peut retourner différents types selon les paramètres d'entrée :

1// Overload signatures with different return types
2function getDinosaurData(id: number): { id: number; species: string; age: number };
3function getDinosaurData(name: string): { name: string; species: string };
4
5// Implementation signature
6function getDinosaurData(identifier: number | string): any {
7  if (typeof identifier === "number") {
8    // Fetching full data based on ID
9    return {
10      id: identifier,
11      species: "Tyrannosaurus",
12      age: 7
13    };
14  } else {
15    // Fetching basic information based on name
16    return {
17      name: identifier,
18      species: "Velociraptor"
19    };
20  }
21}
22
23// Usage with full type information
24const dinosaurById = getDinosaurData(42);
25console.log(dinosaurById.age);  // OK, TypeScript knows that property 'age' exists
26
27const dinosaurByName = getDinosaurData("Blue");
28// console.log(dinosaurByName.age);  // Error, TypeScript knows the object has no 'age' property
29console.log(dinosaurByName.species);  // OK

Remarque que malgré l'utilisation de any dans la signature d'implémentation, TypeScript garantit une sécurité de types complète lors de l'utilisation de la fonction, en s'appuyant sur les signatures de surcharge.

La surcharge avec un nombre différent de paramètres

On peut aussi surcharger des fonctions qui acceptent des nombres de paramètres différents :

1// Overloading with different number of parameters
2function monitorDinosaur(id: number): string;
3function monitorDinosaur(id: number, detailLevel: "basic" | "full"): Object;
4function monitorDinosaur(id: number, detailLevel?: "basic" | "full"): string | Object {
5  if (!detailLevel) {
6    return `Monitoring dinosaur with ID ${id}...`;
7  } else if (detailLevel === "basic") {
8    return {
9      status: "active",
10      location: "Sector B"
11    };
12  } else {
13    return {
14      status: "active",
15      location: "Sector B",
16      speed: "2 km/h",
17      heartRate: 80,
18      bodyTemperature: 38,
19      lastMeal: "2 hours ago",
20      aggressionLevel: "low"
21    };
22  }
23}
24
25// Usage
26const message = monitorDinosaur(5);  // Returns string
27const basicData = monitorDinosaur(5, "basic");  // Returns simple object
28const detailedData = monitorDinosaur(5, "full");  // Returns extended object

La surcharge de méthodes dans les classes

La surcharge de fonctions peut aussi s'appliquer aux méthodes des classes :

1class ResearchTeam {
2  // Overload signatures for sendTeam method
3  sendTeam(target: { x: number, y: number }): string;
4  sendTeam(sector: string): string;
5  sendTeam(target: string, size: number): string;
6
7  // Method implementation
8  sendTeam(target: { x: number, y: number } | string, size?: number): string {
9    if (typeof target === "object") {
10      return `Sending team to coordinates: ${target.x}, ${target.y}`;
11    } else if (size !== undefined) {
12      return `Sending a ${size}-person team to sector: ${target}`;
13    } else {
14      return `Sending standard team to sector: ${target}`;
15    }
16  }
17}
18
19const team = new ResearchTeam();
20console.log(team.sendTeam({ x: 123, y: 456 }));  // Using coordinates
21console.log(team.sendTeam("B-5"));  // Using sector name
22console.log(team.sendTeam("C-7", 5));  // Using sector name and team size

Exemples avancés de surcharge de fonctions

Différents types pour différentes combinaisons de paramètres

1// Overload signatures for complex analysis
2function analyzeBehavior(
3  speciesId: number,
4  behavioralData: number[]
5): { aggressionLevel: number; risk: string };
6
7function analyzeBehavior(
8  speciesId: number,
9  behavioralData: number[],
10  historicalData: boolean
11): { aggressionLevel: number; risk: string; comparison: string };
12
13// Implementation
14function analyzeBehavior(
15  speciesId: number,
16  behavioralData: number[],
17  historicalData?: boolean
18): any {
19  // Basic analysis
20  const averageActivity = behavioralData.reduce((a, b) => a + b, 0) / behavioralData.length;
21  const aggressionLevel = Math.round(averageActivity * 10) / 10;
22
23  let risk = "low";
24  if (aggressionLevel > 7) risk = "high";
25  else if (aggressionLevel > 4) risk = "medium";
26
27  // Basic result
28  const result = { aggressionLevel, risk };
29
30  // Extended result if historical data requested
31  if (historicalData) {
32    // Simulating retrieval of historical data
33    const historicalLevel = 3.5;
34    const change = aggressionLevel - historicalLevel;
35    const comparison = change > 0
36      ? `Increase by ${change.toFixed(1)} pts`
37      : `Decrease by ${Math.abs(change).toFixed(1)} pts`;
38
39    return { ...result, comparison };
40  }
41
42  return result;
43}
44
45// Usage
46const basicAnalysis = analyzeBehavior(1, [2, 4, 3, 6, 1]);
47console.log(basicAnalysis.risk);  // "medium"
48
49const extendedAnalysis = analyzeBehavior(1, [2, 4, 3, 6, 1], true);
50console.log(extendedAnalysis.comparison);  // "Increase by 0.8 pts"

Les surcharges de fonctions generic

Les surcharges de fonctions peuvent être encore plus puissantes lorsqu'elles sont combinées avec des types generic :

1// Generic function overloads
2function getData<T extends { id: number }>(id: number): T;
3function getData<T extends { name: string }>(name: string): T;
4function getData<T>(identifier: number | string): T {
5  // Implementation communicating with the database and returning the appropriate type
6  if (typeof identifier === "number") {
7    // Simulating different data types for different IDs
8    if (identifier < 100) {
9      // Dinosaur
10      return {
11        id: identifier,
12        species: "Tyrannosaurus",
13        age: 7,
14        weight: 7500
15      } as unknown as T;
16    } else {
17      // Employee
18      return {
19        id: identifier,
20        firstName: "Owen",
21        lastName: "Grady",
22        position: "Trainer"
23      } as unknown as T;
24    }
25  } else {
26    // Search by name
27    if (identifier.includes("REX")) {
28      return {
29        name: identifier,
30        species: "Tyrannosaurus",
31        population: 1
32      } as unknown as T;
33    } else {
34      return {
35        name: identifier,
36        type: "Sector",
37        capacity: 12
38      } as unknown as T;
39    }
40  }
41}
42
43// Interfaces for typing
44interface Dinosaur {
45  id: number;
46  species: string;
47  age: number;
48  weight: number;
49}
50
51interface Employee {
52  id: number;
53  firstName: string;
54  lastName: string;
55  position: string;
56}
57
58interface Species {
59  name: string;
60  species: string;
61  population: number;
62}
63
64// Usage with explicit generic type
65const trex = getData<Dinosaur>(1);
66console.log(trex.age);  // OK, TypeScript knows this is a Dinosaur
67
68const employee = getData<Employee>(101);
69console.log(employee.position);  // OK, TypeScript knows this is a Employee
70
71const species = getData<Species>("T-REX-01");
72console.log(species.population);  // OK, TypeScript knows this is a Species

Bonnes pratiques et pièges à éviter

1. La signature d'implémentation doit être compatible avec toutes les signatures de surcharge

Les paramètres de la signature d'implémentation doivent être définis de manière à pouvoir gérer toutes les combinaisons possibles issues des signatures de surcharge :

1// GOOD:
2function test(a: string): number;
3function test(a: number, b: boolean): string;
4function test(a: string | number, b?: boolean): number | string {
5  // Implementation
6  return 0 as any;
7}
8
9// BAD:
10function bad(a: string): number;
11function bad(a: number, b: boolean): string;
12// Error: parameter 'b' in the implementation must be optional
13function bad(a: string | number, b: boolean): number | string {
14  return 0 as any;
15}

2. Les signatures de surcharge sont vérifiées dans l'ordre

TypeScript vérifie les signatures de surcharge de haut en bas : les signatures les plus spécifiques doivent donc être définies avant les plus générales :

1// BAD - more general signature shadows more specific one
2function wrongOrder(data: any[]): number;
3function wrongOrder(data: number[]): string;  // This signature will never be matched
4function wrongOrder(data: any[]): number | string {
5  if (Array.isArray(data) && data.every(d => typeof d === "number")) {
6    return "Array of numbers";
7  } else {
8    return data.length;
9  }
10}
11
12// GOOD - more specific signature comes first
13function correctOrder(data: number[]): string;
14function correctOrder(data: any[]): number;
15function correctOrder(data: any[]): number | string {
16  if (Array.isArray(data) && data.every(d => typeof d === "number")) {
17    return "Array of numbers";
18  } else {
19    return data.length;
20  }
21}

3. Trouver l'équilibre entre surcharge, paramètres optionnels et types union

Parfois, des paramètres optionnels ou des types union peuvent être une meilleure solution que de nombreuses surcharges :

1// Instead of many overloads:
2function option1(id: number): void;
3function option1(id: number, mode: string): void;
4function option1(id: number, mode: string, extra: boolean): void;
5// ...and many more versions
6
7// Better to use optional parameters:
8function option2(id: number, mode?: string, extra?: boolean): void {
9  // Implementation
10}

Exemple pratique : le système de gestion de Jurassic Park

Regardons un exemple complet montrant comment la surcharge de fonctions peut être utilisée dans un système de gestion de Jurassic Park :

1// Defining types used in the system
2type DinosaurStatus = "healthy" | "sick" | "in treatment" | "in a coma";
3type DinosaurSpecies = "Tyrannosaurus" | "Velociraptor" | "Triceratops" | "Brachiosaurus";
4type ThreatLevelType = 1 | 2 | 3 | 4 | 5;
5
6interface BasicData {
7  id: number;
8  name: string;
9  species: DinosaurSpecies;
10  status: DinosaurStatus;
11  threatLevel: ThreatLevelType;
12}
13
14interface HealthData {
15  bodyTemperature: number;
16  heartRate: number;
17  stressFactor: number;
18  lastMeal: Date;
19  weightKg: number;
20}
21
22interface LocationData {
23  sector: string;
24  gpsCoordinates: [number, number];
25  lastUpdated: Date;
26}
27
28interface SecurityReport {
29  fenceStatus: "operational" | "damaged" | "under repair";
30  powerActive: boolean;
31  peopleInSector: number;
32  activeAlerts: string[];
33}
34
35// Class for managing dinosaurs with overloaded methods
36class DinosaurManagementSystem {
37  private database: Map<number, {
38    basic: BasicData;
39    health?: HealthData;
40    location?: LocationData;
41    security?: SecurityReport;
42  }> = new Map();
43
44  // Method for adding a dinosaur to the system
45  addDinosaur(data: BasicData): number;
46  addDinosaur(data: BasicData, health: HealthData): number;
47  addDinosaur(data: BasicData, health: HealthData, location: LocationData): number;
48  addDinosaur(
49    data: BasicData,
50    health?: HealthData,
51    location?: LocationData
52  ): number {
53    const newRecord = {
54      basic: data,
55      health,
56      location: location
57    };
58
59    this.database.set(data.id, newRecord);
60    console.log(`Added dinosaur: ${data.name} (ID: ${data.id})`);
61    return data.id;
62  }
63
64  // Overloaded method for retrieving dinosaur data
65  getData(id: number): BasicData;
66  getData(id: number, type: "health"): HealthData | undefined;
67  getData(id: number, type: "location"): LocationData | undefined;
68  getData(id: number, type: "security"): SecurityReport | undefined;
69  getData(id: number, type: "all"): {
70    basic: BasicData;
71    health?: HealthData;
72    location?: LocationData;
73    security?: SecurityReport;
74  };
75  getData(id: number, type?: "health" | "location" | "security" | "all"): any {
76    const record = this.database.get(id);
77
78    if (!record) {
79      throw new Error(`Dinosaur with ID ${id} does not exist in the system.`);
80    }
81
82    if (!type) {
83      return record.basic;
84    }
85
86    switch (type) {
87      case "health":
88        return record.health;
89      case "location":
90        return record.location;
91      case "security":
92        return record.security;
93      case "all":
94        return record;
95    }
96  }
97
98  // Overloaded method for updating data
99  updateData(id: number, data: Partial<BasicData>): boolean;
100  updateData(id: number, data: HealthData, type: "health"): boolean;
101  updateData(id: number, data: LocationData, type: "location"): boolean;
102  updateData(id: number, data: SecurityReport, type: "security"): boolean;
103  updateData(
104    id: number,
105    data: Partial<BasicData> | HealthData | LocationData | SecurityReport,
106    type?: "health" | "location" | "security"
107  ): boolean {
108    const record = this.database.get(id);
109
110    if (!record) {
111      console.error(`Dinosaur with ID ${id} does not exist in the system.`);
112      return false;
113    }
114
115    if (!type) {
116      // Updating basic data
117      record.basic = { ...record.basic, ...data as Partial<BasicData> };
118      console.log(`Updated basic data for dinosaur ID: ${id}`);
119    } else {
120      switch (type) {
121        case "health":
122          record.health = { ...(record.health || {}), ...data as HealthData };
123          console.log(`Updated health data for dinosaur ID: ${id}`);
124          break;
125        case "location":
126          record.location = { ...(record.location || {}), ...data as LocationData };
127          console.log(`Updated location data for dinosaur ID: ${id}`);
128          break;
129        case "security":
130          record.security = { ...(record.security || {}), ...data as SecurityReport };
131          console.log(`Updated security data for dinosaur ID: ${id}`);
132          break;
133      }
134    }
135
136    this.database.set(id, record);
137    return true;
138  }
139
140  // Overloaded method for generating reports
141  generateReport(): string; // Report for all dinosaurs
142  generateReport(species: DinosaurSpecies): string; // Report for selected species
143  generateReport(threatLevel: ThreatLevelType): string; // Report for threat level
144  generateReport(filter?: DinosaurSpecies | ThreatLevelType): string {
145    let dinosaurs = Array.from(this.database.values()).map(d => d.basic);
146
147    if (filter) {
148      if (typeof filter === "string") {
149        // Filtering by species
150        dinosaurs = dinosaurs.filter(d => d.species === filter);
151        return this.formatReport(`Report for species: ${filter}`, dinosaurs);
152      } else {
153        // Filtering by threat level
154        dinosaurs = dinosaurs.filter(d => d.threatLevel === filter);
155        return this.formatReport(`Report for threat level: ${filter}`, dinosaurs);
156      }
157    }
158
159    return this.formatReport("Full report of all dinosaurs", dinosaurs);
160  }
161
162  private formatReport(title: string, dinosaurs: BasicData[]): string {
163    let report = `=== ${title} ===\n`;
164    report += `Date: ${new Date().toLocaleString()}\n`;
165    report += `Number of dinosaurs: ${dinosaurs.length}\n\n`;
166
167    dinosaurs.forEach(d => {
168      report += `ID: ${d.id} | ${d.name} | ${d.species} | Status: ${d.status} | Threat: ${d.threatLevel}\n`;
169    });
170
171    return report;
172  }
173}
174
175// Example usage of the system
176const system = new DinosaurManagementSystem();
177
178// Adding dinosaurs with different data sets
179const rexId = system.addDinosaur({
180  id: 1,
181  name: "Rexy",
182  species: "Tyrannosaurus",
183  status: "healthy",
184  threatLevel: 5
185});
186
187const raptorId = system.addDinosaur(
188  {
189    id: 2,
190    name: "Blue",
191    species: "Velociraptor",
192    status: "healthy",
193    threatLevel: 4
194  },
195  {
196    bodyTemperature: 38.5,
197    heartRate: 85,
198    stressFactor: 2.1,
199    lastMeal: new Date(Date.now() - 3600000), // 1 hour ago
200    weightKg: 160
201  }
202);
203
204const triceratopsId = system.addDinosaur(
205  {
206    id: 3,
207    name: "Tricy",
208    species: "Triceratops",
209    status: "healthy",
210    threatLevel: 2
211  },
212  {
213    bodyTemperature: 37.2,
214    heartRate: 45,
215    stressFactor: 1.3,
216    lastMeal: new Date(Date.now() - 7200000), // 2 hours ago
217    weightKg: 8500
218  },
219  {
220    sector: "B-7",
221    gpsCoordinates: [34.5678, -118.2345],
222    lastUpdated: new Date()
223  }
224);
225
226// Retrieving different types of data
227const rexBasic = system.getData(rexId);
228console.log(`Dinosaur ${rexBasic.name} has threat level ${rexBasic.threatLevel}`);
229
230const raptorHealth = system.getData(raptorId, "health");
231if (raptorHealth) {
232  console.log(`Body temperature of raptor: ${raptorHealth.bodyTemperature}°C`);
233}
234
235const triceratopsLocation = system.getData(triceratopsId, "location");
236if (triceratopsLocation) {
237  console.log(`Triceratops is located in sector: ${triceratopsLocation.sector}`);
238}
239
240// Updating data
241system.updateData(rexId, { status: "in treatment" });
242
243system.updateData(raptorId,
244  {
245    bodyTemperature: 39.2,
246    heartRate: 95,
247    stressFactor: 4.5,
248    lastMeal: new Date(),
249    weightKg: 162
250  },
251  "health"
252);
253
254// Generating reports
255console.log(system.generateReport());
256console.log(system.generateReport("Velociraptor"));
257console.log(system.generateReport(5));

Résumé

La surcharge de fonctions est un mécanisme puissant de TypeScript qui permet de créer des API flexibles tout en conservant une sécurité de types complète. Tout comme les scientifiques de Jurassic Park doivent adapter leurs procédures à différentes espèces de dinosaures, les développeurs peuvent créer des fonctions qui s'adaptent à différents jeux de paramètres.

Les règles les plus importantes de la surcharge de fonctions :

  1. Définir les signatures de surcharge avant la signature d'implémentation
  2. La signature d'implémentation doit être compatible avec toutes les signatures de surcharge
  3. Placer les signatures les plus spécifiques avant les plus générales
  4. Utiliser la surcharge quand on veut des types de retour différents pour des paramètres d'entrée différents
  5. Envisager des alternatives (paramètres optionnels, types union) pour les cas plus simples

« En programmation, tout comme en génétique des dinosaures », dit le Dr Wu avec un sourire, « la véritable puissance réside dans la capacité à s'adapter. La surcharge de fonctions, c'est un peu comme créer des séquences d'ADN adaptatives : une même fonction peut évoluer pour servir différents objectifs, tout en conservant son intégrité et sa sécurité. »

Code de cette leçon : index.ts
1// Function overloading in Jurassic Park
2console.log("TypeScript function overloading\n");
3
4// ===========================================
5// 1. Basic overloading
6// ===========================================
7console.log("=== BASIC OVERLOADING ===");
8
9// Overload definitions
10function identify(value: string): string;
11function identify(value: number): number;
12function identify(value: boolean): boolean;
13
14// Implémentation
15function identify(value: string | number | boolean): string | number | boolean {
16  return value;
17}
18
19console.log("String:", identify("T-Rex"));
20console.log("Number:", identify(8000));
21console.log("Boolean:", identify(true));
22
23// ===========================================
24// 2. Overloading with different parameters
25// ===========================================
26console.log("\n=== DIFFERENT PARAMETERS ===");
27
28// Overloads
29function createDinosaur(name: string): string;
30function createDinosaur(name: string, species: string): string;
31function createDinosaur(name: string, species: string, age: number): string;
32
33// Implémentation
34function createDinosaur(
35  name: string,
36  species?: string,
37  age?: number
38): string {
39  let result = `Dinosaur: ${name}`;
40  if (species) result += `, Species: ${species}`;
41  if (age !== undefined) result += `, Age: ${age}`;
42  return result;
43}
44
45console.log(createDinosaur("Rexy"));
46console.log(createDinosaur("Blue", "Velociraptor"));
47console.log(createDinosaur("Charlie", "Velociraptor", 3));
48
49// ===========================================
50// 3. Overloading with different returns
51// ===========================================
52console.log("\n=== DIFFERENT RETURN TYPES ===");
53
54interface Dinosaur {
55  name: string;
56  species: string;
57  age: number;
58}
59
60// Overloads with different types de retour
61function findDinosaur(id: number): Dinosaur | undefined;
62function findDinosaur(name: string): Dinosaur[];
63
64// Implémentation
65function findDinosaur(
66  query: number | string
67): Dinosaur | undefined | Dinosaur[] {
68  const database: Dinosaur[] = [
69    { name: "Rexy", species: "T-Rex", age: 5 },
70    { name: "Blue", species: "Velociraptor", age: 3 },
71    { name: "Charlie", species: "Velociraptor", age: 3 }
72  ];
73
74  if (typeof query === "number") {
75    return database[query];
76  } else {
77    return database.filter(d => d.name.includes(query));
78  }
79}
80
81console.log("Find by ID:", findDinosaur(0));
82console.log("Find by name:", findDinosaur("Blue"));
83
84// ===========================================
85// 4. Overloading with objects
86// ===========================================
87console.log("\n=== OVERLOADING WITH OBJECTS ===");
88
89interface SearchByName {
90  type: "name";
91  name: string;
92}
93
94interface SearchBySpecies {
95  type: "species";
96  species: string;
97}
98
99interface SearchByAge {
100  type: "age";
101  minAge: number;
102  maxAge: number;
103}
104
105type SearchCriteria = SearchByName | SearchBySpecies | SearchByAge;
106
107// Overloads
108function search(criteria: SearchByName): Dinosaur[];
109function search(criteria: SearchBySpecies): Dinosaur[];
110function search(criteria: SearchByAge): Dinosaur[];
111
112// Implémentation
113function search(criteria: SearchCriteria): Dinosaur[] {
114  const database: Dinosaur[] = [
115    { name: "Rexy", species: "T-Rex", age: 5 },
116    { name: "Blue", species: "Velociraptor", age: 3 },
117    { name: "Charlie", species: "Velociraptor", age: 3 },
118    { name: "Delta", species: "Velociraptor", age: 4 }
119  ];
120
121  if (criteria.type === "name") {
122    return database.filter(d => d.name === criteria.name);
123  } else if (criteria.type === "species") {
124    return database.filter(d => d.species === criteria.species);
125  } else {
126    return database.filter(
127      d => d.age >= criteria.minAge && d.age <= criteria.maxAge
128    );
129  }
130}
131
132console.log("Search by name:", search({ type: "name", name: "Blue" }));
133console.log("Search by species:", search({ type: "species", species: "Velociraptor" }));
134console.log("Search by age:", search({ type: "age", minAge: 3, maxAge: 4 }));
135
136// ===========================================
137// 5. Overloading methods in classes
138// ===========================================
139console.log("\n=== MÉTHODES DANS LES CLASSES ===");
140
141class DinosaurManager {
142  private dinosaurs: Dinosaur[] = [];
143
144  // Overloads
145  add(dino: Dinosaur): void;
146  add(name: string, species: string, age: number): void;
147
148  // Implémentation
149  add(
150    dinoOrName: Dinosaur | string,
151    species?: string,
152    age?: number
153  ): void {
154    if (typeof dinoOrName === "string") {
155      this.dinosaurs.push({
156        name: dinoOrName,
157        species: species!,
158        age: age!
159      });
160    } else {
161      this.dinosaurs.push(dinoOrName);
162    }
163  }
164
165  getAll(): Dinosaur[] {
166    return [...this.dinosaurs];
167  }
168
169  // Overloads get
170  get(index: number): Dinosaur | undefined;
171  get(name: string): Dinosaur | undefined;
172
173  // Implémentation
174  get(indexOrName: number | string): Dinosaur | undefined {
175    if (typeof indexOrName === "number") {
176      return this.dinosaurs[indexOrName];
177    } else {
178      return this.dinosaurs.find(d => d.name === indexOrName);
179    }
180  }
181}
182
183const manager = new DinosaurManager();
184manager.add({ name: "Rexy", species: "T-Rex", age: 5 });
185manager.add("Blue", "Velociraptor", 3);
186
187console.log("Get by index:", manager.get(0));
188console.log("Get by name:", manager.get("Blue"));
189
190// ===========================================
191// 6. Overloading with Generics
192// ===========================================
193console.log("\n=== GENERICS WITH OVERLOADING ===");
194
195// Overloads
196function process<T extends string>(value: T): string;
197function process<T extends number>(value: T): number;
198function process<T extends boolean>(value: T): boolean;
199
200// Implémentation
201function process<T extends string | number | boolean>(
202  value: T
203): string | number | boolean {
204  if (typeof value === "string") {
205    return value.toUpperCase();
206  } else if (typeof value === "number") {
207    return value * 2;
208  } else {
209    return !value;
210  }
211}
212
213console.log("Process string:", process("hello"));
214console.log("Process number:", process(21));
215console.log("Process boolean:", process(true));
216
217// ===========================================
218// 7. Advanced overloading
219// ===========================================
220console.log("\n=== ADVANCED OVERLOADING ===");
221
222interface FeedOptions {
223  type: "scheduled";
224  time: Date;
225}
226
227interface EmergencyFeedOptions {
228  type: "emergency";
229  reason: string;
230}
231
232// Overloads
233function feedDinosaur(
234  dinosaur: Dinosaur,
235  amount: number
236): void;
237
238function feedDinosaur(
239  dinosaur: Dinosaur,
240  amount: number,
241  options: FeedOptions
242): void;
243
244function feedDinosaur(
245  dinosaur: Dinosaur,
246  amount: number,
247  options: EmergencyFeedOptions
248): void;
249
250// Implémentation
251function feedDinosaur(
252  dinosaur: Dinosaur,
253  amount: number,
254  options?: FeedOptions | EmergencyFeedOptions
255): void {
256  if (!options) {
257    console.log(`Feeding ${dinosaur.name} with ${amount}kg`);
258  } else if (options.type === "scheduled") {
259    console.log(`Scheduled feeding for ${dinosaur.name} at ${options.time.toISOString()}`);
260  } else {
261    console.log(`Emergency feeding for ${dinosaur.name}: ${options.reason}`);
262  }
263}
264
265const trex = { name: "Rexy", species: "T-Rex", age: 5 };
266
267feedDinosaur(trex, 400);
268feedDinosaur(trex, 400, { type: "scheduled", time: new Date() });
269feedDinosaur(trex, 600, { type: "emergency", reason: "Very hungry" });
270
271// ===========================================
272// 8. Constructor overloading
273// ===========================================
274console.log("\n=== CONSTRUCTORS ===");
275
276class Paddock {
277  id: string;
278  name: string;
279  capacity: number;
280
281  // Overloads constructors (syntactically)
282  constructor(id: string);
283  constructor(id: string, name: string);
284  constructor(id: string, name: string, capacity: number);
285
286  // Implémentation
287  constructor(id: string, name?: string, capacity?: number) {
288    this.id = id;
289    this.name = name || `Paddock ${id}`;
290    this.capacity = capacity || 10;
291  }
292
293  getInfo(): string {
294    return `${this.name} (ID: ${this.id}, Capacity: ${this.capacity})`;
295  }
296}
297
298const paddock1 = new Paddock("P-01");
299const paddock2 = new Paddock("P-02", "T-Rex Territory");
300const paddock3 = new Paddock("P-03", "Raptor Pen", 5);
301
302console.log(paddock1.getInfo());
303console.log(paddock2.getInfo());
304console.log(paddock3.getInfo());
305
306// ===========================================
307// 9. Overloading with Promise
308// ===========================================
309console.log("\n=== OVERLOADING WITH PROMISE ===");
310
311// Overloads
312function fetchData(sync: true): Dinosaur;
313function fetchData(sync: false): Promise<Dinosaur>;
314
315// Implémentation
316function fetchData(sync: boolean): Dinosaur | Promise<Dinosaur> {
317  const dino: Dinosaur = {
318    name: "Rexy",
319    species: "T-Rex",
320    age: 5
321  };
322
323  if (sync) {
324    return dino;
325  } else {
326    return Promise.resolve(dino);
327  }
328}
329
330const syncData = fetchData(true);
331console.log("Sync data:", syncData);
332
333fetchData(false).then(asyncData => {
334  console.log("Async data:", asyncData);
335});
336
337// ===========================================
338// 10. Résumé
339// ===========================================
340console.log("\n=== RÉSUMÉ ===");
341console.log("Basic overloading - different signatures");
342console.log("Different parameters - optional arguments");
343console.log("Different returns - type depends on parameters");
344console.log("Objects - discriminated unions");
345console.log("Méthodes dans les classes - method overloading");
346console.log("Generics - overloading with generic types");
347console.log("Constructors - constructor overloading");
348console.log("Promise - synchrone vs asynchrone");
349
350console.log("\nFunction overloading mastered!");

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. Qu’est-ce que la surcharge de fonction en TypeScript ?

  2. 2. Combien de signatures d’implémentation une fonction surchargée peut-elle avoir en TypeScript ?

Voici 2 des 4 questions de cette leçon. Réponds aux autres dans le jeu.

Tâches pratiques dans le jeu

  • Éditeur de code

    Crée une fonction surchargée findDino qui accepte un id (number) ou un name (string) et retourne le résultat approprié.

  • Clique dans l'ordre

    Ordonne les éléments d’une fonction surchargée en TypeScript :

  • Éditeur de code

    Crée une fonction qui prend un paramètre de type string | number et utilise typeof comme garde de type pour retourner la description appropriée du dinosaure.

  • Tri vertical

    Ordonne les interfaces de la plus générale (de base) à la plus spécifique (dérivée) :

  • Clique dans l'ordre

    Ordonne les éléments de la déclaration surchargée de la fonction findDino en TypeScript :

Articles utiles