Transformer la flexibilité de TypeScript : Exploiter la puissance du schéma Map

Découvrez comment améliorer la flexibilité dans la conception TypeScript en utilisant le schéma Map. Apprenez à accroître l'adaptabilité de votre code tout en gardant le contrôle avec les meilleures pratiques.
Transformer la flexibilité de TypeScript : Exploiter la puissance du schéma Map

Dans le domaine dynamique du développement logiciel, il n’est pas rare de rencontrer du code fonctionnel mais rigide. Récemment, je suis tombé sur une implémentation TypeScript qui bien qu’efficace, offrait peu de possibilités d’adaptation. Dans cet article de blog, je vous guiderai à travers mon parcours pour résoudre ce problème en adoptant une solution plus dynamique en utilisant le modèle Map.

Dévoiler la Structure Rigide

Face à ce type particulier de TypeScript, j’ai constaté sa rigidité :

// FinalResponse.ts
import { Reaction } from './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: {
    likes: Reaction
    unicorns: Reaction
    explodingHeads: Reaction
    raisedHands: Reaction
    fire: Reaction
  }
}

Cette configuration reposait sur un type Reaction compagnon :

// Reaction.ts
export type Reaction = {
  count: number
  percentage: number
}

Et elle était intégrée dans la fonction suivante :

// calculator.ts
export const calculateScore = (
  headings: string[],
  sentences: string[],
  words: string[],
  totalPostCharactersCount: number,
  links: { href: string; text: string }[],
  reactions: {
    likes: Reaction
    unicorns: Reaction
    explodingHeads: Reaction
    raisedHands: Reaction
    fire: Reaction
  },
): FinalResponse => {
  // Logique de calcul du score...
}

Le Dilemme de l’Inflexibilité

Imaginez le scénario où vous devez intégrer une nouvelle réaction (par exemple, des cœurs ou des applaudissements). Avec la structure existante, introduire ce changement apparemment mineur implique :

  • De remanier le fichier FinalResponse.ts.
  • De mettre à jour le type Reaction.ts si nécessaire.
  • De modifier la fonction calculateScore.
  • De redéfinir potentiellement d’autres sections dépendantes de l’application.

Une telle étroite interconnexion empêche des changements simples et directs, augmentant le potentiel d’erreur à travers plusieurs fichiers.

Une Réorchestration Dynamique

En repensant l’organisation avec une approche flexible et réutilisable, j’ai proposé une redéfinition élégante :

// FinalResponse.ts
import { Reaction } from './Reaction'

export type ReactionMap = Record<string, 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
}

Analyse Detaillée:

  • ReactionMap : Utilise Record<string, Reaction> pour permettre n’importe quelle chaîne comme clé valide avec une valeur de type Reaction.
  • FinalResponse : Transforme le champ reactions en ReactionMap, permettant l’ajout dynamique sans fouiller dans plusieurs fichiers.

Plaidoyer pour un Code Plus Propre

La transformation dans calculator.ts témoigne d’un codage plus propre et plus adaptable :

// calculator.ts
export const calculateScore = (
  headings: string[],
  sentences: string[],
  words: string[],
  totalPostCharactersCount: number,
  links: { href: string; text: string }[],
  reactions: ReactionMap,
): FinalResponse => {
  // Logique de calcul du score...
}

Équilibrer Flexibilité et Contrôle

Tandis que la nouvelle flexibilité offre de vastes possibilités, elle frôle également le risque d’un usage abusif - des réactions non vérifiées pourraient ouvrir la voie à des chaînes arbitraires.

Trouver un Équilibre Sécurisant

Raffiner la solution en limitant les réactions à des valeurs autorisées spécifiques rehausse la sécurité et la cohérence :

// 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
}

Envisager le Nouveau Paradigme

Visuel 1

Visuel 2

Conclusion de l’Aventure

Voici un procédé qui harmonise flexibilité et contrôle :

  • Flexibilité : Simplifie l’ajout de nouvelles réactions en modifiant uniquement le type AllowedReactions.
  • Contrôle : L’exploitation d’un type union protège contre l’inclusion de réactions invalides.

Cette méthodologie s’aligne avec le principe Ouvert/Fermé, permettant l’extension des fonctionnalités sans modifier les structures fondamentales. Grâce à ce modèle innovant, nous étendons les types de réactions sans effort tout en maintenant une gouvernance sur les actions permises.