Vol. 9 · № 02 · Un atelier TypeScriptA TypeScript workshop

Le coût de la compilation, ou gratuit au runtime n'est pas gratuit The compile cost, or free at runtime isn't free

Les types s'effacent à l'exécution — donc on les croit gratuits. À la compilation, le vérificateur les exécute : récursions déroulées, produits cartésiens matérialisés, structures comparées. Un type trop malin ne ralentit pas ton programme, il ralentit tsc et l'éditeur, à chaque frappe. On mesure avec --extendedDiagnostics ; et au-delà d'une limite, tsc casse, ou cesse de comparer et suppose les types compatibles, en silence. Souvent, un type explicite bat un type malin et lent. Types erase at runtime — so you think them free. At compile time, the checker runs them: unrolled recursions, materialized cartesian products, compared structures. A too-clever type doesn't slow your program, it slows tsc and the editor, on every keystroke. You measure with --extendedDiagnostics; and past a limit, tsc breaks, or stops comparing and silently assumes the types compatible. Often, an explicit type beats a clever, slow one.

AudienceAudience
Dev dont l'éditeur rame et qui ne soupçonne pas ses propres types Dev whose editor lags and who doesn't suspect their own types
Format
Self-paced
ChapitresChapters
5
Date
Juin 2026 Jun 2026
≈ 18 min ●●●● Compile timePerformanceTradeoff
SommaireContents ·
№ 01CadrageFraming3 min

Les types s'effacent à l'exécution — mais ils s'exécutent à la compilation.Types erase at runtime — but they run at compile time.

On répète depuis le Vol 2 que les types s'effacent : zéro coût au runtime. C'est vrai — et c'est la moitié de l'histoire. Au moment de la compilation, le vérificateur exécute réellement tes types : il déroule les récursions, multiplie les unions, compare des structures. Un type trop intelligent ne ralentit pas ton programme, il ralentit tsc et l'éditeur — une seconde de latence à chaque survol, une autocomplétion qui rame. Le prix existe ; il a juste changé d'endroit.Since Vol 2 we've repeated that types erase: zero runtime cost. True — and it's half the story. At compile time, the checker actually runs your types: it unrolls recursions, multiplies unions, compares structures. A too-clever type doesn't slow your program, it slows tsc and the editor — a second of lag on every hover, sluggish autocomplete. The price exists; it just moved.

Un type calculé, profond — et tsc qui rameA computed, deep type — and tsc crawling
// Un type récursif élégant — appliqué à une config profonde :
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

type Draft = DeepPartial<HugeNestedConfig>;   // 12 niveaux d'imbrication
// au survol : "..." après une seconde ; l'autocomplétion rame à chaque frappe
Où va le coût ?Where does the cost go?
Type calculé profondDeep computed type
lourd à la compilationheavy at compile time
survol lent, éditeur qui rameslow hover, sluggish editor
Type écrit, platWritten, flat type
quasi gratuitnear-free
instantané, lisibleinstant, readable

Le runtime ne voit jamais la différence — les deux types s'effacent pareil. Mais l'éditeur, lui, paie : le type récursif se déroule à chaque frappe, le type plat ne coûte rien. Le coût d'un type se mesure en secondes de compilation, pas d'exécution.The runtime never sees the difference — both types erase the same. But the editor pays: the recursive type unrolls on every keystroke, the flat type costs nothing. A type's cost is measured in seconds of compilation, not execution.

Le type écrit à plat — instantané et lisibleThe flat written type — instant and readable
// Le sous-ensemble réellement éditable, écrit à plat :
type Draft = {
  title?: string;
  theme?: { mode?: "light" | "dark" };
};
// au survol : instantané ; tsc ne calcule rien ; on lit la forme d'un coup d'œil
Le réflexe du numéroThe issue's reflex

« Est-ce que je paie le bon prix ? » Un type a un coût de compilation réel — temps de tsc, latence de l'éditeur. Avant d'écrire un type calculé, demande si la garantie vaut la seconde d'attente qu'elle ajoutera à chaque frappe de l'équipe."Am I paying the right price?" A type has a real compile cost — tsc time, editor lag. Before writing a computed type, ask whether the guarantee is worth the second of wait it adds to every keystroke for the team.

Le prolongement du Vol 6.The continuation of Vol 6.

Le Vol 6 prévenait contre le type illisible ; ce numéro en chiffre l'autre coût, mesurable celui-là : le temps de compilation. La lenteur de l'éditeur n'est pas une fatalité ni un mystère — c'est souvent un type trop ambitieux qui se déroule des milliers de fois. On apprend à le voir, et à arbitrer.Vol 6 warned against the unreadable type; this issue quantifies its other, measurable cost: compile time. Editor slowness isn't a fate or a mystery — it's often an over-ambitious type unrolling thousands of times. You learn to see it, and to arbitrate.

№ 02La surpriseThe surprise4 min

Le vérificateur est un interprète. Tes types sont un programme qu'il fait tourner.The checker is an interpreter. Your types are a program it runs.

La surprise vient de la fausse sécurité de l'effacement : « les types disparaissent, donc ils sont gratuits ». Gratuits à l'exécution, oui. Mais le système de types est un petit langage (Vol 6), et tsc en est l'interprète : il déroule les récursions, matérialise les produits cartésiens des templates, compare des structures entières. Un type complexe est un calcul, avec un coût de calcul. La différence avec un programme normal : ce calcul tourne à chaque frappe dans ton éditeur, pas une fois en production.The surprise comes from erasure's false comfort: 'types vanish, so they're free'. Free at runtime, yes. But the type system is a little language (Vol 6), and tsc is its interpreter: it unrolls recursions, materializes templates' cartesian products, compares whole structures. A complex type is a computation, with a computation cost. The difference from a normal program: that computation runs on every keystroke in your editor, not once in production.

Même JavaScript en sortie — coût d'exécution nulSame JavaScript out — zero runtime cost
// À l'exécution : rien. Les deux compilent vers le même JavaScript.
const draft: DeepPartial<Config> = {};
const draft2: { title?: string } = {};
// node ne voit aucune différence — le type a disparu (Vol 2). Coût runtime : nul.

Le type le plus alambiqué et le plus simple produisent le même JavaScript : à l'exécution, ils n'existent plus (Vol 2). C'est cette gratuité-là, réelle, qui endort — on en conclut, à tort, qu'un type complexe ne coûte rien du tout.The most convoluted and the simplest type produce the same JavaScript: at runtime, they no longer exist (Vol 2). It's that real free-ness that lulls — you wrongly conclude a complex type costs nothing at all.

« Les types s'effacent » dit la vérité sur le runtime, et masque le reste. Un type est un programme — et la compilation est son exécution."Types erase" tells the truth about runtime, and hides the rest. A type is a program — and compilation is its execution.
№ 03La mécaniqueThe mechanics5 min

Mesurer avant d'accuser : --extendedDiagnostics nomme le coupable.Measure before blaming: --extendedDiagnostics names the culprit.

On ne devine pas la lenteur, on la mesure. tsc --extendedDiagnostics donne trois chiffres clés : le nombre de Types, le nombre d'Instantiations (combien de fois les génériques se déroulent — souvent le vrai coupable), et le Check time. tsc --generateTrace produit un profil visualisable, qui pointe le type exact qui mange les secondes. Les causes sont presque toujours les mêmes : récursion profonde, produits cartésiens de templates, conditionnels imbriqués, intersections de très gros types. Connaître l'outil, c'est cesser d'accuser au hasard.You don't guess slowness, you measure it. tsc --extendedDiagnostics gives three key numbers: the Types count, the Instantiations count (how many times generics unroll — often the real culprit), and the Check time. tsc --generateTrace produces a viewable profile that points to the exact type eating the seconds. The causes are almost always the same: deep recursion, template cartesian products, nested conditionals, intersections of very large types. Knowing the tool means you stop blaming at random.

Les chiffres qui disent où passe le tempsThe numbers that say where time goes
tsc --extendedDiagnostics
//   Types:            128 304     ← combien de types matérialisés
//   Instantiations:   3 921 556   ← combien de fois les génériques sont déroulés (le coupable)
//   Check time:       7.8 s        ← le temps réel passé à vérifier

tsc --generateTrace ./trace
//   produit un profil ouvrable dans un visualiseur — quel type mange les secondes
Les quatre sources classiques de lenteurThe four classic sources of slowness
// Les sources classiques de lenteur :
type A = DeepRecurse<Big>;            // récursion profonde, redéroulée partout
type B = `${U1}-${U2}-${U3}`;          // produit cartésien de grandes unions (6.3)
type C = CondPyramid<X>;              // conditionnels imbriqués (6.2)
type D = Huge1 & Huge2 & Huge3;       // intersections de très gros objets
Diagnostiquer, puis allégerDiagnose, then lighten
VoirSee
--extendedDiagnosticsTypes, Instantiations, tempsTypes, Instantiations, time
--generateTracele profil, type par typethe profile, type by type
InstantiationsInstantiationsle chiffre à surveillerthe number to watch
AllégerLighten
AplatirFlattenécrire le type, plutôt que le calculerwrite the type, not compute it
interfaceinterfacesouvent plus rapide qu'une intersectionoften faster than an intersection
BornerBoundcouper la récursion / la profondeurcap recursion / depth
Les voisins, seulement nommés.The neighbors, only named.

Une interface étendue est mise en cache et comparée plus efficacement qu'un grand type par intersection — un levier de perf concret. Les project references et le mode incremental évitent de tout revérifier. Et il existe une limite dure de profondeur d'instanciation : au-delà, tsc n'est pas seulement lent — il abandonne, et c'est le sujet du piège.An extended interface is cached and compared more efficiently than a large intersection type — a concrete perf lever. Project references and incremental mode avoid rechecking everything. And there's a hard instantiation-depth limit: beyond it, tsc isn't just slow — it gives up, which is the trap's subject.

№ 04Le piègeThe trap4 min

Le type si malin que tsc renonce — parfois sans le dire.The type so clever tsc gives up — sometimes without saying so.

Le contre-exemple : pousser un type calculé si loin qu'il dépasse la limite de profondeur d'instanciation. Deux issues, toutes deux mauvaises. Soit tsc refuse : « Type instantiation is excessively deep and possibly infinite » — et le code ne compile plus. Soit, plus insidieux, au-delà d'une certaine profondeur de comparaison, il cesse de descendre et suppose les types compatibles, sans erreur visible : on croit avoir un type fort, on a un mensonge lent. La leçon du volume, à son comble : la prouesse au niveau type se paie en secondes — et parfois en garantie perdue.The counter-example: pushing a computed type so far it exceeds the instantiation-depth limit. Two outcomes, both bad. Either tsc refuses: 'Type instantiation is excessively deep and possibly infinite' — and the code stops compiling. Or, more insidiously, past a certain comparison depth, it stops descending and assumes the types compatible, with no visible error: you think you have a strong type, you have a slow lie. The volume's lesson at its peak: type-level feats are paid in seconds — and sometimes in lost guarantee.

Au-delà de la limite : erreur, ou pire, dégradation muettePast the limit: error, or worse, silent degradation
// Un type si profond que TS finit par RENONCER à la précision :
type Flatten<T> = T extends [infer H, ...infer R] ? [H, ...Flatten<R>] : [];
type X = Flatten<VeryLongTuple>;   // au-delà de la limite de profondeur :
//   "Type instantiation is excessively deep and possibly infinite."
//   …ou pire, dans une comparaison trop profonde : TS cesse de descendre et SUPPOSE
//   les types compatibles — une vérification abandonnée, sans un mot

La récursion sans borne franchit la limite : ou bien tsc casse, ou bien il renonce à la précision sans le dire — au-delà d'une profondeur, il suppose les types compatibles au lieu de les comparer, et la garantie qu'on croyait tenir s'est évaporée à l'insu de tous. Lent et faux : le pire des deux.Unbounded recursion crosses the limit: either tsc breaks, or it abandons precision without saying so — past a depth, it assumes the types compatible instead of comparing them, and the guarantee you thought you held evaporated unbeknownst to all. Slow and wrong: the worst of both.

Le coût se paie en équipe, et en continu.The cost is paid as a team, and continuously.

Un type lent ne te coûte pas une seconde une fois : il la coûte à chaque développeur, à chaque frappe, pendant des mois. C'est exactement le même réflexe que tout le long de la série — sur-branding (3.3), unknown partout (4.2), gymnastique du Vol 6 : la cérémonie ne se justifie que par une garantie réelle. Ici, ajoute une variable au calcul : le temps de l'éditeur. Souvent, un type explicite et stable bat un type malin et lent.A slow type doesn't cost you one second once: it costs it to every developer, on every keystroke, for months. It's exactly the same reflex as throughout the series — over-branding (3.3), unknown-everywhere (4.2), Vol 6 gymnastics: ceremony is justified only by a real guarantee. Here, add a variable to the calculus: editor time. Often, an explicit, stable type beats a clever, slow one.

Un type qui dépasse la limite ne te prévient pas toujours — parfois il se contente d'abandonner la précision. Lent se voit ; faux en silence, non.A type that exceeds the limit doesn't always warn you — sometimes it just abandons precision. Slow shows; silently wrong doesn't.
№ 05Bilan & éditoWrap-up & editorial2 min

Le type s'efface au runtime — et s'exécute, lui, à chaque frappe.The type erases at runtime — and runs, itself, on every keystroke.

Les types ne coûtent rien à l'exécution, mais le vérificateur les exécute à la compilation : récursions déroulées, produits cartésiens matérialisés, structures comparées. On mesure avec --extendedDiagnostics (Instantiations en tête) et --generateTrace. Et au-delà d'une limite de profondeur, tsc casse, ou — pire — cesse de comparer et suppose les types compatibles, en silence. Le réflexe : payer le bon prix, et préférer souvent un type explicite et stable à un type malin et lent.Types cost nothing at runtime, but the checker runs them at compile time: unrolled recursions, materialized cartesian products, compared structures. You measure with --extendedDiagnostics (Instantiations first) and --generateTrace. And past a depth limit, tsc breaks, or — worse — stops comparing and silently assumes the types compatible. The reflex: pay the right price, and often prefer an explicit, stable type to a clever, slow one.

Tu veux…You want to…Le gesteThe moveCe que ça coûteWhat it costs
Savoir où passe le tempsKnow where time goestsc --extendedDiagnostics / --generateTrace.tsc --extendedDiagnostics / --generateTrace.Surveiller Instantiations, pas seulement Types.Watch Instantiations, not just Types.
Accélérer un gros typeSpeed up a big typeAplatir, écrire à la main, préférer interface.Flatten, write by hand, prefer interface.Moins de calcul = moins de précision dérivée.Less computation = less derived precision.
Une récursion indispensableA necessary recursionLa borner à une profondeur explicite.Bound it to an explicit depth.Jamais infinie, jamais fonction de l'entrée.Never infinite, never input-dependent.
Ne PAS l'employerNOT use itLe type calculé profond pour la prouesse.The deep computed type for the feat.Lent à chaque frappe ; parfois vérifié à moitié, sans alerte.Slow on every keystroke; sometimes half-checked, silently.
Un type malin se paie une fois en fierté, et mille fois en latence d'éditeur. Le bon prix se compte en secondes d'attente de toute l'équipe.A clever type is paid once in pride, and a thousand times in editor lag. The right price is counted in the whole team's seconds of waiting.
Mot de l'éditeurFrom the editor

« Gratuit au runtime n'est pas gratuit. »"Free at runtime isn't free."

On a tellement répété que les types s'effacent qu'on en a tiré une conclusion fausse : qu'ils sont gratuits. À l'exécution, oui. Mais le système de types est un langage, et tsc son interprète — chaque type complexe est un calcul que le compilateur déroule, et que l'éditeur rejoue à chaque frappe pendant que tu codes. Le coût n'a pas disparu : il a quitté la production pour s'installer dans ta boucle de feedback, là où il pèse le plus. Une autocomplétion qui rame n'est presque jamais une fatalité — c'est, le plus souvent, un type trop ambitieux qu'on n'a pas mesuré.We've so repeated that types erase that we drew a false conclusion: that they're free. At runtime, yes. But the type system is a language, and tsc its interpreter — each complex type is a computation the compiler unrolls, and the editor replays on every keystroke while you code. The cost didn't vanish: it left production to settle into your feedback loop, where it weighs most. Sluggish autocomplete is almost never a fate — it's, most often, an over-ambitious type no one measured.

Reste une dernière vérité, qui prend de la hauteur sur tout le reste et clôt la série. Tout cet édifice — chaque garantie qu'on a apprise à construire, prouver, régler — repose sur des déclarations qu'on n'a pas écrites : les .d.ts des bibliothèques, les @types tiers, les declare qui posent un type sur du JavaScript nu. Et une déclaration peut mentir. Le dernier numéro regarde cette limite ultime : ma garantie n'est jamais plus forte que la déclaration la plus faible sur le chemin — et aux frontières des libs non typées, je suis revenu, exactement, au Vol 7.One last truth remains, rising above all the rest and closing the series. This whole edifice — every guarantee we learned to build, prove, tune — rests on declarations we didn't write: libraries' .d.ts, third-party @types, the declare that puts a type on bare JavaScript. And a declaration can lie. The final issue looks at this ultimate limit: my guarantee is never stronger than the weakest declaration on the path — and at the boundaries of untyped libs, I'm back, exactly, at Vol 7.