React LogoArchitecture front-end
Architecture/Typescript

Exercices TypeScript

L'idée de cet exercice est de vous faire manipuler TypeScript "à nu", sans React au milieu.

Voici deux exercices pratiques sur le typage de flux de données.

Rendez vous sur le playground Typescript et copiez/collez les exercices.

Exercice 1 : Les Type Guards manuels (Niveau Facile)

Le scénario : Vous gérez les paiements d'une application. Vous avez correctement défini vos types avec une Discriminated Union. Maintenant, vous devez créer des fonctions utilitaires (Type Guards) pour vérifier de manière sécurisée le type d'un paiement avant de le traiter.

L'objectif : Écrire les fonctions isCard, isPaypal et isTransfer en utilisant le mot-clé is pour que TypeScript comprenne le rétrécissement de type (Type Narrowing).

type CardPayment = { method: 'card'; cardNumber: string; cvc: number };
type PaypalPayment = { method: 'paypal'; email: string };
type TransferPayment = { method: 'transfer'; iban: string };

type Payment = CardPayment | PaypalPayment | TransferPayment;


// TODO: Typer le retour de cette fonction avec un Type Guard (mot-clé 'is')
const isCard = (element: { method: string }) => {
  return element.method === 'card';
};

function processPayment(payment: Payment) {
  if (isCard(payment)) {
    // Si isCard est bien typé, cette erreur disparaîtra.
    console.log("Paiement par carte : " + payment.cardNumber.slice(-4)); 
  }
}

Exercice 2 : Le Générateur de Type Guards (Niveau Expert)

Le scénario : Votre application grandit. Vous avez maintenant 15 méthodes de paiement différentes. Écrire 15 fonctions isQuelqueChose à la main devient fastidieux et source d'erreurs si on ajoute une nouvelle méthode.

L'objectif : Créer un objet guards généré dynamiquement qui contiendra toutes nos fonctions de vérification (guards.isCard, guards.isPaypal, etc.). Vous allez devoir utiliser des Mapped Types, l'utilitaire Capitalize et Extract.

C'est un exercice complexe, prenez votre temps et lisez bien les erreurs du compilateur.

type CardPayment = { method: 'card'; cardNumber: string; cvc: number };
type PaypalPayment = { method: 'paypal'; email: string };
type TransferPayment = { method: 'transfer'; iban: string };

type Payment = CardPayment | PaypalPayment | TransferPayment;
type PaymentMethod = Payment['method']; // 'card' | 'paypal' | 'transfer'

const paymentsMethods: PaymentMethod[] = ['card', 'paypal', 'transfer'];


// TODO 1 : Créer le type dynamique "Guards". 
// Il doit générer les clés isCard, isPaypal, isTransfer et leur associer une fonction Type Guard générique.
type Guards = {
  // Votre magie TypeScript ici
};

// TODO 2 : Implémenter le reduce pour générer l'objet (en le castant "as Guards")
const guards = paymentsMethods.reduce((acc, payment) => {
  // Implémentation
}, {} );

/* --- LE TEST --- */
function processDynamicPayment(payment: Payment) {
  if (guards.isCard(payment)) {
    // TypeScript DOIT savoir que c'est une carte ici
    console.log("Paiement par carte : " + payment.cardNumber.slice(-4)); 
  }
}