Ne vous trompez pas de type

Simplifiez votre TypeScript : pourquoi l'utilisation de Record<K,V> est préférable aux types intermédiaires superflus pour un code plus maintenable et robuste.
Loin de moi, l'idée de lyncher un confrère qui a voulu bien faire en rédigeant un article sur le typage Typescript. Ce dernier a voulu partager une optimisation de code dans un article Medium afin de réduire l'impact d'une PR qui s'étendait sur plusieurs fichiers. J'ai d'autant plus peur quand je vois la centaine de commentaires qui acclament cet article, j'espère vraiment que ce sont des bots parce que das le cas contraire, on a du soucis à se faire sur nos projets ! Je rédige donc cet article afin de vous montrer une approche plus pratique.
Quelque part, je comprends le besoin initial, on a tous, la phobie du merge conflict quand on travaille dans une grosse équipe, donc on fait le maximum pour réduire l'impact.
Pourtant, son approche ne me parait pas judicieuse et je vais vous expliquer pourquoi.
Disclaimer: N'allez surtout pas le harceler dans les commentaires, je pense qu'il a voulu bien faire et aider la communauté. Malheureusement, son manque d'expérience peut coûter cher à terme si vous optez pour son approche.
Je ne vais pas revenir sur son état de code initial qui était justement ce qu'il cherchait à optimiser, mais directement vous montrer le résultat final après refacto.
Voici le typage proposé
L'élémet qui nous intéresse, c'est la clé "reactions" dans le type FinalResponse.
// FinalResponse.ts
import { Reaction } from './Reaction'
type AllowedReactions =
| 'likes'
| 'unicorns'
| 'explodingHeads'
| 'raisedHands'
| 'fire'
export type ReactionMap = {
[key in AllowedReactions]: Reaction
}
export type FinalResponse = {
totalScore: number
headingsPenalty: number
sentencesPenalty: number
charactersPenalty: number
wordsPenalty: number
headings: string[]
sentences: string[]
words: string[]
links: { href: string; text: string }[]
exceeded: {
exceededSentences: string[]
repeatedWords: { word: string; count: number }[]
}
reactions: ReactionMap
}
Pourtant quand je lis cette approche, je trouve cela complexe et pas optimisé, la création d'un type supplémentaire ReactionMap me semble superflu.
J'aurais procédé de la sorte:
// FinalResponse.ts
import { Reaction } from './Reaction'
type AllowedReactions =
| 'likes'
| 'unicorns'
| 'explodingHeads'
| 'raisedHands'
| 'fire'
export type FinalResponse = {
totalScore: number
headingsPenalty: number
sentencesPenalty: number
charactersPenalty: number
wordsPenalty: number
headings: string[]
sentences: string[]
words: string[]
links: { href: string; text: string }[]
exceeded: {
exceededSentences: string[]
repeatedWords: { word: string; count: number }[]
}
reactions: Record<AllowedReactions, Reaction>
}
Pourquoi privilégier un typage simple et lisible ?
- Premièrement, il faut réduire au maximum le nombre de types intermédiaires (surtout quand on peut s'en passer)
- Et parce que lorsque l'on na moins de types à maintenir et que chacun de ces types est correctement utilisé, cela réduit le scope du projet et le rend simple.
J'applique avec beaucoup de ferveur l'approche de Dijkstra qui dit que la simplicité est un prédicat de la fiabilité.
Ce n'est que lorsque vous manipulez des choses simples, que vous créez du code fiable, robuste!
N'essayez pas de produire quelque chose de confus et difficile à lire. Certains pensent que le code complexe est synonyme de maitrise, mais je suis persuadé qu'il reflète surtout une incompréhension de l'auteur.

Alexandre P.
Développeur passionné depuis plus de 20 ans, j'ai une appétence particulière pour les défis techniques et changer de technologie ne me fait pas froid aux yeux.
Poursuivre la lecture dans la rubrique Dev