Lesson 10/10 12 min

TS in the real world

any piercing the net vs unknown forcing checks, the DOM, API responses — and the TS + AI workflow.

Semaine 3 : TypeScript est là, mais il ne protège plus rien

Tu es en plein projet. Semaine 1, tu as tout typé proprement. Semaine 2, les délais ont serré. Semaine 3 : les any se sont multipliés « pour aller vite ». Réponses d'API, event handlers, données de formulaires : tout est passé en any. TypeScript est toujours installé, le compilateur tourne, mais il ne bloque plus rien.

Le problème, c'est qu'any est contagieux. Toute valeur issue d'une expression any devient any à son tour : le trou s'étend de proche en proche dans le code, silencieusement, jusqu'à ce que la prod plante.

const data: any = fetchReponseApi();   // ← any

const nom = data.utilisateur.nom;      // ← any aussi
const longueur = nom.length;           // ← any aussi, toujours
longueur.toFixed(2);                   // ← aucune erreur TS, crash au runtime

any ne désactive pas juste la prochaine vérification : il désactive toute la chaîne. Chaque variable qui touche un any sort du radar du compilateur. C'est exactement comme débrancher le détecteur de fumée pour ne plus entendre le bip : le feu n'a pas disparu.

Prédis avant de lire

Même code avec brut: any au lieu de unknown : que dit le compilateur, et que fait lireMessage(42) à l'exécution ?

Voir la réponse

Avec any, le compilateur dit ✓ 0 erreur : il a éteint toute vérification. Mais à l'exécution, 42.toUpperCase() n'existe pas : TypeError. unknown donne la même souplesse d'entrée mais refuse le code tant que la preuve typeof n'est pas là : c'est exactement la différence entre percer le filet et le tendre.

any vs unknown : le duel

Face à une donnée dont tu ignores la forme (réponse d'API, paramètre JSON, input utilisateur), tu as deux options :

  • any : « fais ce que tu veux, je ne regarde plus ». Le compilateur pose les armes. C'est le trou dans le filet, rappelé depuis la leçon 1.
  • unknown : « je ne sais pas ce que c'est, donc prouve avant d'utiliser ». Toute opération est refusée tant qu'un narrowing (leçon 4) n'a pas établi le type.
function lireMessage(brut: any): string {
  return brut.toUpperCase();   // ← any : pas d'erreur TS, TypeError si brut = 42
}

function lireMessageSur(brut: unknown): string {
  return brut.toUpperCase();   // ← ✗ 'brut' is of type 'unknown'
  // Le compilateur refuse : prouve d'abord que c'est une string
}

La règle : pour toute donnée externe (API, JSON, formulaire), partir sur unknown par défaut. Tu ne perds rien en souplesse d'entrée ; tu gagnes l'obligation de valider avant d'utiliser.

any est un emprunt à taux usuraire : tu vas vite maintenant, tu rembourses en bugs runtime plus tard. unknown est le même service (accepter n'importe quelle entrée), avec le filet tendu sous tes pieds.

Dans la vraie vie, les équipes utilisent des bibliothèques de validation comme Zod : elles vérifient la forme des données au runtime et en déduisent le type TypeScript d'un coup. Le principe reste exactement unknown + preuve ; Zod automatise juste la partie fastidieuse.

À toi : force la preuve

Le code ci-dessous déclare brut: unknown. Clique sur Vérifier les types : le compilateur va refuser brut.toUpperCase() sans garde. Lis l'erreur, ajoute le narrowing, et revérifie jusqu'au ✓ 0 erreur. Remplace mentalement unknown par any : le check passerait... et lireMessage(42) crasherait. unknown t'a forcé à écrire le code qui gère le 42.

🧐 Labo TypeScript · le compilateur juge ton code (mode strict)
Bloqué sur le fix ? Voir la correction

Le garde typeof brut === 'string' prouve au compilateur que c'est une string dans la branche. Un repli String(brut) gère tout le reste :

function lireMessage(brut: unknown): string {
  if (typeof brut === 'string') {
    return brut.toUpperCase();
  }
  return String(brut);
}

Résultat : ✓ 0 erreur. À l'exécution : « BONJOUR » puis « 42 ».

Le workflow TS + IA : 4 réflexes

Ce cours t'a fait voyager de la leçon 1 (le contrat) jusqu'ici (les frontières de l'app). En clôture, voici les 4 réflexes qui font tenir l'ensemble quand tu codes avec une IA :

  1. Type d'abord. Écris l'interface ou la signature, puis demande le code à l'IA. Le contrat cadre la génération : l'IA voit ce que tu attends et produit du code qui s'y conforme. Sans contrat écrit, elle improvise.
  2. Strict toujours. "strict": true dans ton tsconfig.json, sans négocier. C'est le mode qui active la protection maximale : sans lui, TS laisse passer des trous que tu ne vois pas.
  3. Tout écart signalé par le compilateur est une vraie question. Jamais de as any pour faire taire une erreur (leçon 1). Le compilateur désigne un problème réel : c'est lui qu'il faut résoudre.
  4. Aux frontières, unknown + validation. API, JSON, formulaires : toute donnée qui entre de l'extérieur commence en unknown, puis est prouvée (leçons 4 et 8) avant usage.

Rappel important : les types n'attrapent pas les erreurs de logique bien typée. Un calcul faux peut être parfaitement typé. Ton second filet de sécurité, c'est les tests : cours Tester son code.

Conclusion du cours : compilateur + tests, ce sont les deux relecteurs infatigables de ton code et de celui de l'IA. Le compilateur attrape les contrats trahis, les tests attrapent la logique fausse. Ensemble, ils te laissent aller vite sans avoir peur.

Une donnée inconnue arrive face à deux guichets : guichet ANY (barrière levée, la donnée passe direct, explosion rouge au runtime) et guichet UNKNOWN (poste de contrôle vert : preuve typeof requise, puis sortie sûre). ? donnée inconnue Guichet ANY barrière levée en permanence 💥 TypeError crash au runtime Guichet UNKNOWN preuve typeof requise ✓ passage autorisé ✓ Sortie sûre type garanti
Les deux guichets : any lève la barrière en permanence ; unknown exige la preuve avant de laisser passer.

Week 3: TypeScript is there, but it no longer protects anything

You're deep in a project. Week 1, everything was typed cleanly. Week 2, the deadline tightened. Week 3: any has spread everywhere "to move fast". API responses, event handlers, form data: all slapped with any. TypeScript is still installed, the compiler still runs, but it blocks nothing.

The problem is that any is contagious. Every value derived from an any expression becomes any in turn: the hole spreads silently across your codebase until prod blows up.

const data: any = fetchApiResponse();   // ← any

const name = data.user.name;            // ← any too
const length = name.length;             // ← any too, still
length.toFixed(2);                      // ← no TS error, crash at runtime

any doesn't just disable the next check — it disables the entire chain. Every variable that touches an any falls off the compiler's radar. It's exactly like unplugging the smoke detector so you stop hearing the beep — the fire hasn't gone away.

Predict before reading on

Same code with raw: any instead of unknown: what does the compiler say, and what does readMessage(42) do at runtime?

Show the answer

With any, the compiler says ✓ 0 errors: it has switched off all checking. But at runtime, 42.toUpperCase() doesn't exist: TypeError. unknown gives the same input flexibility but refuses the code until the typeof proof is in place — that's exactly the difference between piercing the net and keeping it taut.

any vs unknown: the duel

When facing data whose shape you don't know (API response, JSON param, user input), you have two options:

  • any: "do whatever you want, I'm not watching". The compiler puts down its weapons. It's the hole in the net, recalled from lesson 1.
  • unknown: "I don't know what this is, so prove it before using it". Every operation is rejected until a narrowing (lesson 4) has established the type.
function readMessage(raw: any): string {
  return raw.toUpperCase();   // ← any: no TS error, TypeError if raw = 42
}

function readMessageSafe(raw: unknown): string {
  return raw.toUpperCase();   // ← ✗ 'raw' is of type 'unknown'
  // Compiler refuses: prove it's a string first
}

The rule: for any external data (API, JSON, forms), start with unknown by default. You lose nothing in input flexibility; you gain the obligation to validate before using.

any is a high-interest loan — you move fast now, you repay in runtime bugs later. unknown is the same service — accepting any input — with the safety net kept taut under your feet.

In the real world, teams use validation libraries like Zod: they check the shape of data at runtime and infer the TypeScript type in one shot. The principle is still exactly unknown + proof — Zod just automates the tedious part.

Your turn: force the proof

The code below uses raw: unknown. Click Check the types: the compiler will reject raw.toUpperCase() without a guard. Read the error, add the narrowing, and re-check until ✓ 0 errors. Try mentally replacing unknown with any: the check would pass... and readMessage(42) would crash. unknown forced you to write the code that handles the 42.

🧐 TypeScript lab · the compiler judges your code (strict mode)
Stuck on the fix? Show it

The guard typeof raw === 'string' proves to the compiler it's a string in that branch. A fallback String(raw) handles everything else:

function readMessage(raw: unknown): string {
  if (typeof raw === 'string') {
    return raw.toUpperCase();
  }
  return String(raw);
}

Result: ✓ 0 errors. At runtime: "HELLO" then "42".

The TS + AI workflow: 4 reflexes

This course took you from lesson 1 (the contract) all the way here (app boundaries). To close, here are the 4 reflexes that hold everything together when you code with an AI:

  1. Type first. Write the interface or signature, then ask the AI for the code. The contract frames the generation: the AI sees what you expect and produces code that conforms. Without a written contract, it improvises.
  2. Strict always. "strict": true in your tsconfig.json, non-negotiable. This is the mode that enables maximum protection: without it, TS lets through holes you don't see.
  3. Every compiler error is a real question. Never as any to silence an error (lesson 1). The compiler is pointing at a real problem: that's what needs solving.
  4. At boundaries, unknown + validation. APIs, JSON, forms: any data coming from outside starts as unknown, then is proven (lessons 4 and 8) before use.

Important reminder: types don't catch logic errors in well-typed code. A wrong calculation can be perfectly typed. Your second safety net is tests: the Testing your code course.

Course conclusion: compiler + tests are the two tireless reviewers of your code and the AI's. The compiler catches betrayed contracts, tests catch wrong logic. Together, they let you move fast without fear.

Unknown data arrives at two checkpoints: the ANY gate (barrier always up, data passes straight through, red explosion at runtime) and the UNKNOWN gate (green checkpoint: typeof proof required, then safe exit). ? unknown data ANY Gate barrier always up 💥 TypeError crash at runtime UNKNOWN Gate typeof proof required ✓ passage granted ✓ Safe Exit type guaranteed
Two gates: any keeps the barrier permanently up; unknown demands proof before letting through.

🎯 Pratique

S'entraîner (clique pour ouvrir) :

💬 Ré-explique sans regarder
Ré-explique sans regarder

Explique le duel any/unknown avec l'image du filet. Couvre : any = trou (et contagieux) ; unknown = même souplesse d'entrée, utilisation interdite sans preuve ; narrowing = la preuve ; données externes = unknown par défaut.

Une bonne explication dit : any perce le filet et est contagieux (tout ce qui le touche devient any). unknown accepte la même variété d'entrées, mais interdit toute utilisation tant que le narrowing n'a pas prouvé le type. Pour toute donnée externe (API, JSON, formulaire) : partir sur unknown par défaut.
🧠 Rappel libre
Rappel libre

De mémoire : les 4 réflexes du workflow TS + IA vus dans ce cours.

sans négocier) ; 3. Jamais as any pour faire taire une erreur : c'est une vraie question à résoudre ; 4. Aux frontières, unknown + validation. Et les tests restent le second filet pour la logique." data-i18n-html-en="1. Type first (write the contract before asking the AI for code); 2. Strict always (\"strict\": true, non-negotiable); 3. Never as any to silence an error: it's a real question to solve; 4. At boundaries, unknown + validation. And tests remain the second net for logic.">1. Type d'abord (écrire le contrat avant de demander le code à l'IA) ; 2. Strict toujours ("strict": true sans négocier) ; 3. Jamais as any pour faire taire une erreur : c'est une vraie question à résoudre ; 4. Aux frontières, unknown + validation. Et les tests restent le second filet pour la logique.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

En fin de projet, l'IA propose : « Pour nettoyer les 40 erreurs TypeScript restantes d'un coup, remplaçons les types stricts par any dans les fichiers concernés. On retypera plus tard. » Tu acceptes, ou tu rejettes ?

À rejeter. Les 40 erreurs sont 40 questions réelles que le compilateur pose. Les éteindre en masse avec any, c'est expédier 40 bugs potentiels au runtime. Et « plus tard » n'arrive jamais : any est contagieux, le retypage coûtera bien plus cher que la correction au fil de l'eau. Chaque erreur mérite une réponse, pas un silence forcé.
Différence entre any et unknown ?
Pour une réponse d'API dont tu ne maîtrises pas la forme, le bon type de départ ?
Pourquoi dit-on qu'any est contagieux ?
Le workflow TS + IA en une phrase ?
Next step

The compiler catches betrayed contracts, but not well-typed wrong logic: a wrong calculation can be perfectly typed. Your second tireless reviewer is tests. If you haven't taken it yet, it's the natural next step.

Course: Testing your code →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement