Les projets TypeScript s'agrandissent. Les fichiers se multiplient. Les dépendances s'entremêlent. Et finit par arriver un moment où votre build se heurte à un mur qui n'a rien à voir avec la complexité de votre logique, mais tout à voir avec le fait que le compilateur doit lire l'univers entier avant de pouvoir écrire un seul fichier de déclaration.
TypeScript 6.0 répond à ce problème avec isolatedDeclarations. Cette fonctionnalité repense la manière dont les fichiers .d.ts sont générés. Au lieu de lier l'émission de déclarations au pipeline complet de vérification des types, elle permet au compilateur d'émettre ces fichiers en examinant chaque fichier source de manière isolée. Le résultat est un processus de build capable de s'exécuter en parallèle sur des milliers de fichiers, plutôt que de parcourir votre graphe de dépendances lien par lien.
Le véritable goulot d'étranglement
Actuellement, la génération de fichiers de déclaration est une opération séquentielle. Lorsque vous activez --declaration et lancez le compilateur, TypeScript ne peut pas émettre un fichier .d.ts pour un module donné tant qu'il n'a pas pleinement compris chaque type que ce module touche. Si utils.ts importe des types de types.ts, et que types.ts tire quelque chose de api.ts, le compilateur doit résoudre cette chaîne avant de pouvoir décrire ce que utils.ts exporte.
Dans un large monorepo, cette cascade est brutale. Un seul fichier situé près de la racine de votre graphe d'importation peut bloquer l'émission de déclarations pour des centaines de fichiers en aval. Votre processeur possède huit cœurs, mais sept d'entre eux restent inactifs pendant que TypeScript reconstruit laborieusement la forme de chaque interface à travers les limites des packages. Le compilateur effectue un travail nécessaire, mais le couplage entre la vérification des types et l'émission de déclarations signifie que vous payez le prix fort de l'analyse multi-fichiers, même lorsque vous souhaitez seulement que les types de l'interface publique soient écrits sur le disque.
Comment isolatedDeclarations change la donne
isolatedDeclarations brise ce couplage. Lorsque le flag est activé, le compilateur accepte d'émettre un fichier .d.ts pour un fichier source sans demander à aucun autre fichier ce qu'il signifie. Il y parvient en exigeant un contrat simple : chaque symbole exporté doit porter une annotation de type explicite et visible à l'endroit où il est déclaré.
Si le compilateur peut voir le type complet écrit directement dans la source, il n'a pas besoin de procéder à l'inférence. Il n'a pas besoin de traquer les imports. Il n'a pas besoin de savoir si l'identifiant User dans un autre fichier est une interface, un alias de type ou une classe. Il émet simplement exactement ce que vous avez écrit.
Cela signifie que le fichier A et le fichier B peuvent générer leurs déclarations simultanément. Un orchestrateur de build peut confier chaque fichier à un thread séparé. Les transpileurs rapides qui ignoraient auparavant la génération de .d.ts faute de disposer d'un vérificateur de types complet peuvent désormais produire des fichiers de déclaration eux aussi, car le travail devient purement syntaxique.
Le compromis : écrivez-le explicitement
La vitesse n'est pas gratuite. Vous devez cesser de compter sur l'inférence de type pour tout ce que vous exportez. Chaque fonction, classe, variable et constante publique doit avoir son type explicitement spécifié. Si TypeScript doit calculer le type en examinant une instruction return ou en résolvant un argument générique, isolatedDeclarations générera une erreur.
Voici à quoi cela ressemble en pratique. Sans le flag, vous pourriez écrire :
export function fetchUser(id: number) {
return fetch(`/users/${id}`).then(r => r.json());
}
TypeScript infère le type de retour en inspectant fetch, puis Promise.prototype.then, puis la fonction anonyme qui retourne r.json(). Pour émettre un .d.ts, le compilateur doit effectuer toute cette analyse.
Avec isolatedDeclarations activé, vous devez annoter l'export :
interface User {
id: number;
email: string;
}
export function fetchUser(id: number): Promise<User> {
return fetch(`/users/${id}`).then(r => r.json());
}
Désormais, le compilateur voit immédiatement Promise<User>. Il émet la déclaration et passe à la suite.
Cette règle s'applique largement. Les tableaux exportés ont besoin de types explicites au lieu de les déduire de leurs éléments. Les objets exportés nécessitent des annotations de type explicites si leur structure importe pour les consommateurs. Les fonctions génériques doivent avoir leurs types de retour et leurs contraintes visibles au site de déclaration. Vous ne pouvez pas exporter le résultat d'un type mappé complexe sans lui donner un alias de type nommé qui est écrit en entier.
L'avantage est que votre API publique devient auto-documentée. Les consommateurs — et le compilateur — n'ont plus besoin de rétro-concevoir votre intention à partir des détails d'implémentation. Les types constituent un contrat délibéré.
Où passe le temps gagné
Dans une base de code importante, l'impact est immédiat. Des temps de build qui s'étiraient sur plusieurs minutes peuvent tomber à quelques secondes, car l'émission de déclarations cesse d'être le principal facteur de ralentissement. Chaque fichier est émis indépendamment, le processus s'adapte donc au nombre de cœurs dont vous disposez, et non à la profondeur de votre graphe d'importation.
This also changes what tools you can use. Transpilers like esbuild and swc are already lightning-fast at turning TypeScript into JavaScript, but many teams still run tsc separately just to produce .d.ts files. With isolatedDeclarations, those fast tools can handle both jobs. They do not need to replicate TypeScript's entire type system to generate declarations; they only need to parse syntax and copy the explicit types you provided. That makes end-to-end TypeScript builds with alternative toolchains far more viable.
Distributed and incremental builds get simpler too. In continuous integration, a remote cache or a sharded build can emit declarations for a package without downloading its full transitive dependency graph first. If the types are explicit in the source, the build shard has everything it needs.
What Stays the Same
The constraint applies only to exports. Inside a module, life continues as normal. Local variables, private class members, and unexported helper functions can still rely on full type inference. TypeScript will happily infer the type of a loop variable or a closure parameter without complaint.
export function calculateTotal(items: Item[]): number {
// Local variable: inference is fine
const taxRate = 0.08;
// Private class member inside a local class: inference is fine
class Helper {
private cache = new Map();
}
return items.reduce((sum, item) => sum + item.price * (1 + taxRate), 0);
}
Only the exported function signature needed an annotation. The internal machinery stays loose and expressive. This keeps the authoring burden tolerable. You are not switching to a fully explicit style everywhere; you are simply formalizing the contract at the boundary of each module.
Is It Right for Your Codebase?
Adopting isolatedDeclarations shifts where you spend your time. You invest a few extra keystrokes when you write an export, and in exchange you stop paying interest on every build. For library authors, this is often an easy sell. Public APIs should probably be annotated anyway. For application developers working inside a closed monorepo, the upfront cost can feel like unnecessary ceremony. But if your team measures build time in coffee breaks, the trade becomes attractive quickly.
You can adopt it incrementally. Enable the flag, run the compiler, and fix the errors it surfaces on exported symbols. The error messages tell you exactly which public-facing types are implicit. Fix those, leave the internals alone, and watch your declaration step accelerate.
One thing to remember: this flag does not make TypeScript's type checker itself faster. If you want quicker feedback in your editor or faster tsc --noEmit runs, you still need project references, stricter file inclusion, or other architectural fixes. isolatedDeclarations specifically targets the emission of .d.ts files. It is a build optimization, not a type-checking optimization.
The Real Takeaway
isolatedDeclarations asks you to treat your public types as first-class artifacts. Stop making the compiler deduce them. Write them down. Once you do, the compiler stops crawling through your entire dependency graph every time it needs to generate a declaration file. It emits in parallel, tools like esbuild and swc handle full TypeScript workflows, and your monorepo builds stop dragging.
The cost moves from build time to authoring time. For most growing teams, that is a trade worth making.
