forked from labs42io/clean-code-typescript
-
Notifications
You must be signed in to change notification settings - Fork 2
/
README.md
3144 lines (2368 loc) · 79.6 KB
/
README.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# clean-code-typescript [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Clean%20Code%20Typescript&url=https://github.com/labs42io/clean-code-typescript)
Concepts de "Clean Code (ou code propre)" adaptés à TypeScript.
Inspirés par [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript).
Ce document est une traduction de ce repository rédigé par [labs42io](https://github.com/labs42io/clean-code-typescript).
## Sommaire
1. [Introduction](#introduction)
2. [Variables](#variables)
3. [Fonctions](#fonctions)
4. [Objets et Structures de Données](#objets-et-structures-de-données)
5. [Classes](#classes)
6. [SOLID](#solid)
7. [Tests](#tests)
8. [Opérations Concurrentes](#opérations-concurrentes)
9. [Gestion des erreurs](#gestion-des-erreurs)
10. [Formatage du Code](#formatage-du-code)
11. [Commentaires](#commentaires)
12. [Traductions](#traductions)
## Introduction
![Humorous image of software quality estimation as a count of how many expletives
you shout when reading code](https://www.osnews.com/images/comics/wtfm.jpg)
Principes de l'ingénierie de logiciels, extraits du livre de Robert C. Martin
[*Clean Code*](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882),
adaptés à TypeScript. Ce n'est pas un guide de style. C'est un guide pour créer des logiciels
[lisibles, réutilisables, et faciles à réorganiser](https://github.com/ryanmcdermott/3rs-of-software-architecture)
en utilisant TypeScript.
Il n'est pas nécessaire de suivre strictement tous les principes mentionés ici,
et sans oublier que peu d'entre eux seront universellement acceptés. Ce ne sont
que des lignes de recommendations et rien de plus, mais ce sont celles codifiées
au cours de nombreuses années d'expérience collective par les auteurs de *Clean Code*.
Notre métier en tant qu'ingénieur de logiciels a un peu plus de 50 ans et on a encore
beaucoup à apprendre. Lorsque l'architecture de logiciels est aussi ancienne que
l'architecture elle-même, on aura peut-être alors des règles plus difficiles à suivre.
Pour l'instant, laissez ce guide vous servir de pierre de touche pour évaluer la
qualité du code TypeScript que vous et votre équipe produisez.
Une dernière chose: les connaître ne fera pas immédiatement de vous un meilleur
développeur de logiciels, et travailler avec eux pendant de nombreuses années ne
signifie pas que vous ne commettrez pas d’erreurs. Chaque portion de code commence
comme un premier échantillon, comme de l'argile humide qui prend sa forme à la fin.
Enfin, on chasse ces imperfections quand on les revise ensemble avec notre équipe.
Ne vous tracassez pas à tout faire durant vos premiers éssais. Cherchez plutôt à
améliorer le code mis en place!
**[⬆ retour en haut](#sommaire)**
## Variables
### Utiliser des noms de variables descriptifs
Distinguer les noms de façon que le lecteur sache ce que les différences offrent.
**Mal:**
```ts
function between<T>(a1: T, a2: T, a3: T): boolean {
return a2 <= a1 && a1 <= a3;
}
```
**Bien:**
```ts
function between<T>(value: T, left: T, right: T): boolean {
return left <= value && value <= right;
}
```
**[⬆ retour en haut](#sommaire)**
### Utiliser des noms de variables prononçables
Si vous ne pouvez pas le prononcer, vous ne pouvez pas en discuter sans ressembler à un idiot.
**Mal:**
```ts
type DtaRcrd102 = {
genymdhms: Date;
modymdhms: Date;
pszqint: number;
}
```
**Bien:**
```ts
type Customer = {
generationTimestamp: Date;
modificationTimestamp: Date;
recordId: number;
}
```
**[⬆ retour en haut](#sommaire)**
### Utiliser le même vocabulaire pour le même type de variable
**Mal:**
```ts
function getUserInfo(): User;
function getUserDetails(): User;
function getUserData(): User;
```
**Bien:**
```ts
function getUser(): User;
```
**[⬆ retour en haut](#sommaire)**
### Utiliser des noms consultables
Nous lirons plus de code que nous n'en aurons jamais écrit. Il est important que
le code que nous écrivons soit lisible et consultable. En *ne nommant pas* les
variables qui finissent par être significatives pour comprendre notre programme,
nous blessons nos lecteurs. Rendez vos noms consultables. Des outils comme
[TSLint](https://palantir.github.io/tslint/rules/no-magic-numbers/) peuvent aider
à identifier les constantes sans nom.
**Mal:**
```ts
// What the heck is 86400000 for?
setTimeout(restart, 86400000);
```
**Bien:**
```ts
// Declare them as capitalized named constants.
const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;
setTimeout(restart, MILLISECONDS_IN_A_DAY);
```
**[⬆ retour en haut](#sommaire)**
### Utiliser des variables explicatives
**Mal:**
```ts
declare const users: Map<string, User>;
for (const keyValue of users) {
// iterate through users map
}
```
**Bien:**
```ts
declare const users: Map<string, User>;
for (const [id, user] of users) {
// iterate through users map
}
```
**[⬆ retour en haut](#sommaire)**
### Évitez le mapping mental
Mieux vaut être explicite qu'implicite.
*La clarté est essentielle.*
**Mal:**
```ts
const u = getUser();
const s = getSubscription();
const t = charge(u, s);
```
**Bien:**
```ts
const user = getUser();
const subscription = getSubscription();
const transaction = charge(user, subscription);
```
**[⬆ retour en haut](#sommaire)**
### Ne pas ajouter de contexte inutile
Si votre nom de classe/type/objet vous dit quelque chose, ne répétez pas cela dans
le nom de votre variable.
**Mal:**
```ts
type Car = {
carMake: string;
carModel: string;
carColor: string;
}
function print(car: Car): void {
console.log(`${car.carMake} ${car.carModel} (${car.carColor})`);
}
```
**Bien:**
```ts
type Car = {
make: string;
model: string;
color: string;
}
function print(car: Car): void {
console.log(`${car.make} ${car.model} (${car.color})`);
}
```
**[⬆ retour en haut](#sommaire)**
### Utiliser des arguments par défaut au lieu d'un raccourci ou de conditions
Les arguments par défaut sont souvent plus propres que les raccourcis.
**Mal:**
```ts
function loadPages(count?: number) {
const loadCount = count !== undefined ? count : 10;
// ...
}
```
**Bien:**
```ts
function loadPages(count: number = 10) {
// ...
}
```
**[⬆ retour en haut](#sommaire)**
### Utiliser enum pour documenter l'intention
Les énumérations peuvent vous aider à documenter l'intention du code. Par exemple,
lorsque on veut que les valeurs soient différentes plutôt que la valeur exacte de
celles-ci.
**Mal:**
```ts
const GENRE = {
ROMANTIC: 'romantic',
DRAMA: 'drama',
COMEDY: 'comedy',
DOCUMENTARY: 'documentary',
}
projector.configureFilm(GENRE.COMEDY);
class Projector {
// declaration of Projector
configureFilm(genre) {
switch (genre) {
case GENRE.ROMANTIC:
// some logic to be executed
}
}
}
```
**Bien:**
```ts
enum GENRE {
ROMANTIC,
DRAMA,
COMEDY,
DOCUMENTARY,
}
projector.configureFilm(GENRE.COMEDY);
class Projector {
// declaration of Projector
configureFilm(genre) {
switch (genre) {
case GENRE.ROMANTIC:
// some logic to be executed
}
}
}
```
**[⬆ retour en haut](#sommaire)**
## Fonctions
### Paramètres d'une fonction (2 or moins idéalement)
Limiter le nombre de paramètres d'une fonction est extrêmement important car cela
facilite le test de votre fonction. En avoir plus de trois conduit à une explosion
combinatoire où chaque argument doit être testé séparément dans des tonnes de cas
différents.
Avoir un ou deux paramètres par fonction est idéal, et trois devraient être évités
si possible. Rien de plus que cela devrait être consolidé. Assez souvent, si vous
avez plus de deux arguments, votre fonction essaie d'en faire trop à la fois.
Dans les cas où ce n'est pas le cas, la plupart du temps, un objet de niveau
supérieur suffit comme argument.
Pensez à utiliser des objets littéraux si vous avez besoin de beaucoup plus de paramètres.
Pour rendre évidentes les attributs que la fonction attend, vous pouvez utiliser
la syntaxe de [déstructuration](https://basarat.gitbook.io/typescript/future-javascript/destructuring).
Cela présente quelques avantages :
1. Quand quelqu'un regarde la signature d’une fonction, il est immédiatement clair
lesquels des attributs sont en train d’être utilisées.
2. Il peut être utilisé pour simuler des paramètres avec des noms.
3. La déstructuration clone également les valeurs primitives spécifiées de l'objet
passé comme paramètre dans la fonction. Cela peut aider à prévenir les effets secondaires.
Remarque: les objets et les tableaux qui sont déstructurés à partir de l'objet
argument ne sont PAS clonés.
4. TypeScript vous avertit des attributs non-utilisés, qui seraient impossibles
sans déstructuration.
**Mal:**
```ts
function createMenu(title: string, body: string, buttonText: string, cancellable: boolean) {
// ...
}
createMenu('Foo', 'Bar', 'Baz', true);
```
**Bien:**
```ts
function createMenu(options: { title: string, body: string, buttonText: string, cancellable: boolean }) {
// ...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
```
Vous pouvez encore améliorer la lisibilité en utilisant [type aliases (ou type d'alias)](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases):
```ts
type MenuOptions = { title: string, body: string, buttonText: string, cancellable: boolean };
function createMenu(options: MenuOptions) {
// ...
}
createMenu({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
});
```
**[⬆ retour en haut](#sommaire)**
### Les fonctions devraient faire une chose
C'est de loin la règle la plus importante en ingénierie de logiciels. Lorsque
les fonctions font plus d'une chose, elles sont plus difficiles à composer, à
tester et à raisonner. Lorsque vous arrivez à isoler une fonction afin d’exécuter
une seule tâche, elle peut être facilement refactorisée et votre code sera beaucoup
plus net. Si vous ne prenez en compte ce qui est dit dans ce guide, vous serez en
avance sur de nombreux développeurs.
**Mal:**
```ts
function emailClients(clients: Client[]) {
clients.forEach((client) => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
```
**Bien:**
```ts
function emailClients(clients: Client[]) {
clients.filter(isActiveClient).forEach(email);
}
function isActiveClient(client: Client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
```
**[⬆ retour en haut](#sommaire)**
### Les noms de fonction doivent préciser ce qu'ils font
**Mal:**
```ts
function addToDate(date: Date, month: number): Date {
// ...
}
const date = new Date();
// It's hard to tell from the function name what is added
addToDate(date, 1);
```
**Bien:**
```ts
function addMonthToDate(date: Date, month: number): Date {
// ...
}
const date = new Date();
addMonthToDate(date, 1);
```
**[⬆ retour en haut](#sommaire)**
### Les fonctions ne doivent avoir qu'un niveau d'abstraction
Quand vous avez plus d'un niveau d'abstraction, votre fonction en fait généralement
trop. La division des fonctions conduit à une réutilisabilité et à des tests plus faciles.
**Mal:**
```ts
function parseCode(code: string) {
const REGEXES = [ /* ... */ ];
const statements = code.split(' ');
const tokens = [];
REGEXES.forEach((regex) => {
statements.forEach((statement) => {
// ...
});
});
const ast = [];
tokens.forEach((token) => {
// lex...
});
ast.forEach((node) => {
// parse...
});
}
```
**Bien:**
```ts
const REGEXES = [ /* ... */ ];
function parseCode(code: string) {
const tokens = tokenize(code);
const syntaxTree = parse(tokens);
syntaxTree.forEach((node) => {
// parse...
});
}
function tokenize(code: string): Token[] {
const statements = code.split(' ');
const tokens: Token[] = [];
REGEXES.forEach((regex) => {
statements.forEach((statement) => {
tokens.push( /* ... */ );
});
});
return tokens;
}
function parse(tokens: Token[]): SyntaxTree {
const syntaxTree: SyntaxTree[] = [];
tokens.forEach((token) => {
syntaxTree.push( /* ... */ );
});
return syntaxTree;
}
```
**[⬆ retour en haut](#sommaire)**
### Eliminer la partie dupliquée du code
Faites de votre mieux pour éviter la duplication de certaines parties du votre code.
La duplication du code est mauvaise car cela signifie qu'il y a plus d'un endroit
pour modifier quelque chose si vous devez changer une logique.
Imaginez que vous dirigiez un restaurant et que vous gardiez une trace de votre
inventaire: toutes vos tomates, oignons, ail, épices, etc. Si vous avez plusieurs
listes sur lesquelles vous gardez cela, alors toutes doivent être mises à jour
lorsque vous servez un plat avec des tomates. Si vous n'avez qu'une seule liste,
il n'y a qu'un seul endroit à mettre à jour!
Souvent, vous avez du code en double parce que vous avez deux ou plusieurs choses
légèrement différentes, qui partagent beaucoup en commun, mais leurs différences
vous obligent à avoir deux ou plusieurs fonctions distinctes qui font à peu près
les mêmes choses. Supprimer le code en double signifie créer une abstraction qui
peut gérer cet ensemble de choses différentes avec une seule fonction/module/classe.
Avoir la correcte abstraction est essentiel, c'est pourquoi vous devez suivre les
principes [SOLID](#solid). Les mauvaises abstractions peuvent être pires que le
code en double, alors faites attention! Cela dit, si vous pouvez faire une bonne
abstraction, faites-le! Ne vous répétez pas, sinon vous vous retrouverez à actualiser
plusieurs endroits chaque fois que vous voulez changer une chose.
**Mal:**
```ts
function showDeveloperList(developers: Developer[]) {
developers.forEach((developer) => {
const expectedSalary = developer.calculateExpectedSalary();
const experience = developer.getExperience();
const githubLink = developer.getGithubLink();
const data = {
expectedSalary,
experience,
githubLink
};
render(data);
});
}
function showManagerList(managers: Manager[]) {
managers.forEach((manager) => {
const expectedSalary = manager.calculateExpectedSalary();
const experience = manager.getExperience();
const portfolio = manager.getMBAProjects();
const data = {
expectedSalary,
experience,
portfolio
};
render(data);
});
}
```
**Bien:**
```ts
class Developer {
// ...
getExtraDetails() {
return {
githubLink: this.githubLink,
}
}
}
class Manager {
// ...
getExtraDetails() {
return {
portfolio: this.portfolio,
}
}
}
function showEmployeeList(employee: Developer | Manager) {
employee.forEach((employee) => {
const expectedSalary = employee.calculateExpectedSalary();
const experience = employee.getExperience();
const extra = employee.getExtraDetails();
const data = {
expectedSalary,
experience,
extra,
};
render(data);
});
}
```
Vous devez être critique sur la duplication de code. Parfois, il y a un compromis
entre le code dupliqué et une complexité accrue en introduisant une abstraction
inutile. Lorsque deux implémentations de deux modules différents se ressemblent
mais vivent dans des domaines différents, la duplication peut être acceptable et
préférable à l'extraction du code commun. Le code commun extrait dans ce cas
introduit une dépendance indirecte entre les deux modules.
**[⬆ retour en haut](#sommaire)**
### Définir les objets par défaut avec “Object.assign” ou déstructuration
**Mal:**
```ts
type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean };
function createMenu(config: MenuConfig) {
config.title = config.title || 'Foo';
config.body = config.body || 'Bar';
config.buttonText = config.buttonText || 'Baz';
config.cancellable = config.cancellable !== undefined ? config.cancellable : true;
// ...
}
createMenu({ body: 'Bar' });
```
**Bien:**
```ts
type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean };
function createMenu(config: MenuConfig) {
const menuConfig = Object.assign({
title: 'Foo',
body: 'Bar',
buttonText: 'Baz',
cancellable: true
}, config);
// ...
}
createMenu({ body: 'Bar' });
```
Également, vous pouvez utiliser la déstructuration avec des valeurs par défaut:
```ts
type MenuConfig = { title?: string, body?: string, buttonText?: string, cancellable?: boolean };
function createMenu({ title = 'Foo', body = 'Bar', buttonText = 'Baz', cancellable = true }: MenuConfig) {
// ...
}
createMenu({ body: 'Bar' });
```
Pour éviter tout effet secondaire et tout comportement inattendu en transmettant
explicitement la valeur `undefined` ou `null`, vous pouvez dire au compilateur de
TypeScript de ne pas l'autoriser.
Consultez [`--strictNullChecks`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-0.html#--strictnullchecks) l'option dans TypeScript.
**[⬆ retour en haut](#sommaire)**
### Ne pas utiliser pas des indicateurs comme paramètres de fonction
Les indicateurs indiquent à votre utilisateur que cette fonction fait plus d'une chose.
Les fonctions devraient faire une chose. Divisez vos fonctions si elles suivent des
chemins de code différents basés sur un booléen.
**Mal:**
```ts
function createFile(name: string, temp: boolean) {
if (temp) {
fs.create(`./temp/${name}`);
} else {
fs.create(name);
}
}
```
**Bien:**
```ts
function createTempFile(name: string) {
createFile(`./temp/${name}`);
}
function createFile(name: string) {
fs.create(name);
}
```
**[⬆ retour en haut](#sommaire)**
### Éviter les effets secondaires (partie 1)
Une fonction produit un effet secondaire si elle fait autre chose que de prendre
une valeur et de renvoyer une ou plusieurs autres valeurs. Un effet secondaire
pourrait être d'écrire dans un fichier, de modifier une variable globale ou de
transférer accidentellement tout votre argent à un étranger.
Maintenant, vous devez avoir des effets secondaires dans un programme à l'occasion.
Comme dans l'exemple précédent, vous devrez peut-être écrire dans un fichier. Ce
que vous voulez faire, c'est de centraliser où vous faites cela. Ne pas avoir
plusieurs fonctions et classes qui écrivent dans un fichier particulier. Avoir
un service qui le fait. Seul et l'unique.
Le point principal est d'éviter les pièges courants comme le partage d'état entre
des objets sans aucune structure, l'utilisation de types de données mutables qui
peuvent être écrits par n'importe quoi, et ne pas centraliser où se produisent
vos effets secondaires. Si vous pouvez le faire, vous serez plus heureux que la
grande majorité des autres programmeurs.
**Mal:**
```ts
// Global variable referenced by following function.
let name = 'Robert C. Martin';
function toBase64() {
name = btoa(name);
}
toBase64();
// If we had another function that used this name, now it'd be a Base64 value
console.log(name); // expected to print 'Robert C. Martin' but instead 'Um9iZXJ0IEMuIE1hcnRpbg=='
```
**Bien:**
```ts
const name = 'Robert C. Martin';
function toBase64(text: string): string {
return btoa(text);
}
const encodedName = toBase64(name);
console.log(name);
```
**[⬆ retour en haut](#sommaire)**
### Éviter les effets secondaires (partie 2)
En JavaScript, les primitives sont passées par valeur et les objets et tableaux
sont passés par référence. Dans le cas d'objets et de tableaux, si votre fonction
modifie un tableau de panier d'achat, par exemple, en ajoutant un article à acheter,
alors toute autre fonction qui utilise ce tableau `cart` sera affectée par cet
ajout. C'est peut-être bien, mais ça peut aussi être mauvais. Imaginons une mauvaise situation:
L'utilisateur clique sur le bouton “Achat”, qui appelle une fonction `purchase`
qui génère une demande réseau et envoie le tableau `cart` au serveur. En raison
d'une mauvaise connexion réseau, la fonction d'achat doit continuer à réessayer
la demande. Maintenant, que se passe-t-il si, dans l'intervalle, l'utilisateur
clique accidentellement sur le bouton `addItemToCart` sur un article qu'il ne
souhaite pas avant le début de la demande réseau? Si cela se produit et que la
demande de réseau commence, alors cette fonction d'achat enverra l'article ajouté
accidentellement car il a une référence à un tableau de panier d'achat que la
fonction `addItemToCart` a modifié en ajoutant un article indésirable.
Une excellente solution serait que `addItemToCart` clone toujours le`cart`, le
modifie et renvoie le clone. Cela garantit qu'aucune autre fonction conservant
une référence du panier ne sera affectée par des modifications.
Deux avertissements à mentionner à cette approche:
1. Il peut y avoir des cas où vous souhaitez réellement modifier l'objet passée
comme paramètre, mais lorsque vous adoptez cette pratique de programmation, vous
constaterez que ces cas sont assez rares. La plupart des choses peuvent être
refactorisées pour n'avoir aucun effet secondaire!
(voir [fonction pure](https://en.wikipedia.org/wiki/Pure_function))
2. Le clonage de gros objets peut être très coûteux en termes de performances.
Heureusement, ce n'est pas un gros problème dans la pratique, car il existe d'excellentes
bibliothèques qui permettent à ce type d'approche de programmation d'être rapide
et moins gourmande en mémoire que pour le clonage manuel d'objets et de tableaux.
**Mal:**
```ts
function addItemToCart(cart: CartItem[], item: Item): void {
cart.push({ item, date: Date.now() });
};
```
**Bien:**
```ts
function addItemToCart(cart: CartItem[], item: Item): CartItem[] {
return [...cart, { item, date: Date.now() }];
};
```
**[⬆ retour en haut](#sommaire)**
### Ne pas écrire dans les fonctions globales
Polluer les fonctions globales est une mauvaise pratique en JavaScript car vous
pourriez entrer en conflit avec une autre bibliothèque et l'utilisateur de votre
API ne serait pas plus sage jusqu'à ce qu'il obtienne une exception en production.
Réfléchissons à un exemple: et si vous vouliez étendre la méthode native Array
de JavaScript pour avoir une méthode `diff` qui pourrait montrer la différence
entre deux tableaux? Vous pouvez écrire votre nouvelle fonction dans le
`Array.prototype`, mais elle pourrait entrer en conflit avec une autre bibliothèque
qui a essayé de faire la même chose. Et si cette autre bibliothèque utilisait
simplement `diff` pour trouver la différence entre le premier et le dernier élément
d'un tableau? C'est pourquoi il serait beaucoup mieux d'utiliser simplement des
classes et d'étendre simplement le global `Array`.
**Mal:**
```ts
declare global {
interface Array<T> {
diff(other: T[]): Array<T>;
}
}
if (!Array.prototype.diff) {
Array.prototype.diff = function <T>(other: T[]): T[] {
const hash = new Set(other);
return this.filter(elem => !hash.has(elem));
};
}
```
**Bien:**
```ts
class MyArray<T> extends Array<T> {
diff(other: T[]): T[] {
const hash = new Set(other);
return this.filter(elem => !hash.has(elem));
};
}
```
**[⬆ retour en haut](#sommaire)**
### Privilégier la programmation fonctionnelle à la programmation impérative
Privilégiez ce style de programmation quand vous le pouvez.
**Mal:**
```ts
const contributions = [
{
name: 'Uncle Bobby',
linesOfCode: 500
}, {
name: 'Suzie Q',
linesOfCode: 1500
}, {
name: 'Jimmy Gosling',
linesOfCode: 150
}, {
name: 'Gracie Hopper',
linesOfCode: 1000
}
];
let totalOutput = 0;
for (let i = 0; i < contributions.length; i++) {
totalOutput += contributions[i].linesOfCode;
}
```
**Bien:**
```ts
const contributions = [
{
name: 'Uncle Bobby',
linesOfCode: 500
}, {
name: 'Suzie Q',
linesOfCode: 1500
}, {
name: 'Jimmy Gosling',
linesOfCode: 150
}, {
name: 'Gracie Hopper',
linesOfCode: 1000
}
];
const totalOutput = contributions
.reduce((totalLines, output) => totalLines + output.linesOfCode, 0);
```
**[⬆ retour en haut](#sommaire)**
### Encapsuler les conditions
**Mal:**
```ts
if (subscription.isTrial || account.balance > 0) {
// ...
}
```
**Bien:**
```ts
function canActivateService(subscription: Subscription, account: Account) {
return subscription.isTrial || account.balance > 0;
}
if (canActivateService(subscription, account)) {
// ...
}
```
**[⬆ retour en haut](#sommaire)**
### Éviter les conditions négatives
**Mal:**
```ts
function isEmailNotUsed(email: string): boolean {
// ...
}
if (isEmailNotUsed(email)) {
// ...
}
```
**Bien:**
```ts
function isEmailUsed(email: string): boolean {
// ...
}
if (!isEmailUsed(node)) {
// ...
}
```
**[⬆ retour en haut](#sommaire)**
### Éviter les conditions
Cela semble être une tâche impossible. En entendant cela pour la première fois,
la plupart des gens disent: “Comment suis-je censé faire quoi que ce soit sans
une déclaration `if`?" La réponse est que vous pouvez utiliser le polymorphisme
pour réaliser la même tâche dans de nombreux cas. La deuxième question est
généralement, "c'est bien, mais pourquoi voudrais-je faire ça?" La réponse est
un concept de code propre précédent que nous avons appris: une fonction ne devrait
faire qu'une seule chose. Lorsque vous avez des classes et des fonctions qui ont
des instructions `if`, vous dites à votre utilisateur que votre fonction fait
plus d'une chose. N'oubliez pas, faites juste une chose.
**Mal:**
```ts
class Airplane {
private type: string;
// ...
getCruisingAltitude() {
switch (this.type) {
case '777':
return this.getMaxAltitude() - this.getPassengerCount();
case 'Air Force One':
return this.getMaxAltitude();
case 'Cessna':
return this.getMaxAltitude() - this.getFuelExpenditure();
default:
throw new Error('Unknown airplane type.');