Cours JavaScript et TypeScript · Module 6: Bases de TypeScript
La surcharge de fonctions
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 :
- Les signatures de surcharge : les définitions des types de paramètres et de valeurs de retour
- 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 typeDans l'exemple ci-dessus :
- On a défini deux signatures de surcharge : l'une acceptant
number, l'autre acceptantstring - On a créé une seule implémentation qui gère les deux types
- 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); // OKRemarque 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 objectLa 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 sizeExemples 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 SpeciesBonnes 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 :
- Définir les signatures de surcharge avant la signature d'implémentation
- La signature d'implémentation doit être compatible avec toutes les signatures de surcharge
- Placer les signatures les plus spécifiques avant les plus générales
- Utiliser la surcharge quand on veut des types de retour différents pour des paramètres d'entrée différents
- 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. Qu’est-ce que la surcharge de fonction en TypeScript ?
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 :