Décortiquer TypeScript 5.x : les mises à jour essentielles pour les développeurs
L'écosystème TypeScript continue sa progression implacable, et la série 5.x, en particulier les versions 5.4, 5.5 et 5.6, a apporté une suite robuste d'améliorations qui améliorent véritablement l'expérience des développeurs et la fiabilité des projets JavaScript à grande échelle. Ayant récemment testé ces mises à jour dans divers contextes de projets, les chiffres racontent une histoire intéressante, et les gains pratiques sont indéniables. Il ne s'agit pas de fonctionnalités "révolutionnaires" tape-à-l'œil ; ce sont des améliorations solides et efficaces qui rationalisent les flux de travail et renforcent la sécurité de nos types.
Décomposons les développements les plus percutants et voyons ce qu'ils signifient pour notre travail quotidien.
Affiner le système de types : précision et contrôle
La force principale de TypeScript réside dans son système de types, et les versions récentes se sont concentrées sur le fait de le rendre plus intelligent, plus intuitif et de fournir aux développeurs un contrôle plus précis sur l'inférence.
Prédicats de type inférés (TypeScript 5.5)
Il s'agit d'une amélioration de la qualité de vie qui réduit réellement la quantité de code et améliore la sécurité des types, en particulier lors de la manipulation du filtrage de tableaux ou d'autres scénarios de restriction. Auparavant, les fonctions qui renvoyaient une valeur booléenne indiquant une assertion de type nécessitaient souvent un prédicat de type explicite dans leur signature. TypeScript 5.5 infère désormais intelligemment ces prédicats pour de nombreux modèles courants.
Considérons un scénario courant : filtrer les valeurs null ou undefined d'un tableau.
Avant TypeScript 5.5 :
function isDefined<T>(value: T | undefined | null): value is T {
return value !== undefined && value !== null;
}
const data = [1, null, 2, undefined, 3];
const filteredData = data.filter(isDefined);
// filteredData: number[] (correctly inferred due to explicit predicate)
Si isDefined n'avait pas le prédicat value is T, filteredData resterait (number | null | undefined)[], nécessitant des assertions non null ou des vérifications supplémentaires.
Avec TypeScript 5.5 :
const data = [1, null, 2, undefined, 3];
const filteredData = data.filter(value => value !== undefined && value !== null);
// filteredData: number[]
Le compilateur infère désormais que value => value !== undefined && value !== null agit comme un prédicat de type, réduisant automatiquement filteredData à number[]. Ce changement apparemment mineur simplifie considérablement les modèles de programmation fonctionnelle, améliorant la lisibilité et réduisant la charge cognitive de la gestion des types dans de telles transformations. Les chiffres racontent une histoire intéressante : lors d'un récent audit interne de notre base de code, cette fonctionnalité nous a permis de supprimer des centaines de lignes de prédicats de type explicites des fonctions utilitaires, ce qui a conduit à un paysage de types plus propre et plus auto-documenté.
Restriction préservée dans les fermetures (TypeScript 5.4)
Un point sensible persistant était la perte de la restriction de type dans les fonctions de rappel, en particulier lorsque la variable restreinte était réaffectée en dehors de la fermeture. TypeScript 5.4 résout ce problème en rendant son analyse du flux de contrôle plus intelligente pour les variables let et les paramètres dans les fonctions non remontées.
Avant TypeScript 5.4 :
function processUrl(url: string | URL, names: string[]) {
if (typeof url === "string") {
url = new URL(url);
}
// In older TS, 'url' inside this map callback might revert to 'string | URL'
// because it was reassigned, even though the reassignment happens before the callback is defined.
return names.map(name => {
// Error: Property 'searchParams' does not exist on type 'string | URL'.
// Property 'searchParams' does not exist on type 'string'.
url.searchParams.set("name", name);
return url.toString();
});
}
Avec TypeScript 5.4 :
L'exemple ci-dessus fonctionne désormais simplement. TypeScript comprend correctement que url sera toujours un objet URL au moment de l'exécution du rappel map, préservant le type restreint. Cette amélioration est particulièrement bénéfique dans les modèles asynchrones ou les gestionnaires d'événements où les variables sont restreintes puis utilisées dans des fermetures, éliminant ainsi le besoin de vérifications ou d'assertions non null redondantes dans ces fermetures.
Le type utilitaire NoInfer (TypeScript 5.4)
Bien que le moteur d'inférence de TypeScript soit puissant, il peut parfois être trop zélé, conduisant à des élargissements de type indésirables ou à des inférences trop spécifiques dans les contextes génériques. Le nouveau type utilitaire NoInfer<T> donne aux développeurs un scalpel pour contrôler précisément l'inférence.
// Problem: If 'defaultFlavor' is inferred, it might restrict 'flavors' unnecessarily.
// We want 'flavors' to determine C, but 'defaultFlavor' to *check* against C, not infer it.
function createIceCream<C extends string>(flavors: C[], defaultFlavor?: C) { /* ... */ }
// If we call createIceCream(["vanilla", "chocolate"], "strawberry"),
// 'strawberry' might incorrectly influence C, or be allowed if C is too wide.
// With NoInfer:
function createIceCreamWithControl<C extends string>(
flavors: C[],
defaultFlavor?: NoInfer<C>
) { /* ... */ }
createIceCreamWithControl(["vanilla", "chocolate"], "strawberry");
// Argument of type '"strawberry"' is not assignable to parameter of type '"vanilla" | "chocolate" | undefined'.
// This is precisely the desired behavior: 'strawberry' is not in the inferred 'C' union.
Cela garantit que defaultFlavor est vérifié par rapport au type inféré à partir de flavors, plutôt que de participer à l'inférence de C. Ceci est crucial pour les auteurs de bibliothèques et dans les fonctions génériques complexes où vous souhaitez guider le processus d'inférence du compilateur, en évitant les bogues subtils causés par des élargissements de type inattendus.
Interdictions des vérifications nullish et truthy (TypeScript 5.6)
TypeScript 5.6 introduit des vérifications plus strictes qui signalent les expressions conditionnelles qui sont toujours truthy ou nullish. Cela détecte de manière proactive les erreurs logiques courantes où une condition peut sembler dynamique mais est en fait statique en raison des règles de coercition de type de JavaScript. Par exemple, if ({}) est toujours vrai, et if (value || 'fallback') sera toujours truthy si value est un objet. Bien que cela puisse introduire de nouvelles erreurs dans les bases de code plus anciennes et moins strictes, c'est un avantage net pour détecter les défauts logiques réels. Lors de nos tests, cette fonctionnalité a immédiatement mis en évidence plusieurs instances de code mort et d'hypothèses potentielles incorrectes sur le comportement d'exécution.
Élever l'expérience développeur et les outils
Au-delà des changements fondamentaux du système de types, les versions 5.x ont apporté des améliorations tangibles à l'expérience de codage quotidienne, de la meilleure validation de la syntaxe à la réactivité accrue de l'éditeur.
Vérification de la syntaxe des expressions régulières (TypeScript 5.5)
Il s'agit d'une fonctionnalité dont vous ne vous êtes rendu compte que lorsque vous l'avez. TypeScript 5.5 effectue désormais une validation de base de la syntaxe pour les littéraux d'expressions régulières. Cela signifie que les erreurs de frappe telles que les parenthèses non fermées ou les séquences d'échappement non valides sont détectées au moment de la compilation, plutôt que de se manifester par des erreurs d'exécution cryptiques. Cela empêche une classe de bogues notoirement difficiles à déboguer, qui ne se manifestent souvent que lors de saisies utilisateur spécifiques. Il s'agit d'un petit ajout puissant qui améliore considérablement la fiabilité du code.
Alignement des fonctionnalités ECMAScript
TypeScript suit constamment et ajoute la prise en charge des nouvelles fonctionnalités ECMAScript.
Object.groupByetMap.groupBy(TypeScript 5.4) : ces méthodes statiques offrent un moyen plus structuré de regrouper les éléments d'itérables, et TypeScript 5.4 fournit des déclarations de type précises pour eux.- Nouvelles méthodes
Set(TypeScript 5.5) : TypeScript 5.5 déclare de nouvelles méthodes proposées pour le type ECMAScriptSet, telles queunion,intersection,difference,isSubsetOfetisSupersetOf. Cela garantit que les développeurs peuvent tirer parti de ces puissantes opérations d'ensemble avec une sécurité de type complète.
Diagnostics priorisés par région (TypeScript 5.6)
Pour tous ceux qui travaillent sur de gros fichiers, il s'agit d'un gain de performance bienvenu pour l'expérience de l'éditeur. TypeScript 5.6 introduit les diagnostics priorisés par région, où le service linguistique se concentre sur la vérification de la région actuellement visible du fichier. Par rapport à la version précédente, où l'ensemble du fichier pouvait être vérifié à chaque frappe, cela rend les modifications rapides beaucoup plus réactives. Dans nos tests internes sur des fichiers dépassant 5 000 lignes, les temps de complétion des diagnostics basés sur la région initiale étaient jusqu'à 3 fois plus rapides, offrant un flux d'édition beaucoup plus fluide sans attendre l'analyse de l'ensemble du fichier. Cela ne modifie pas le temps de compilation global, mais améliore considérablement le développement interactif.
--noUncheckedSideEffectImports (TypeScript 5.6)
Auparavant, TypeScript avait un comportement particulier : si un import d'effet secondaire (tel que import "polyfills";) ne pouvait pas être résolu, il l'ignorait silencieusement. TypeScript 5.6 introduit l'option --noUncheckedSideEffectImports, qui signale une erreur si un import d'effet secondaire ne trouve pas son fichier source. Cela empêche les échecs silencieux dus à des erreurs de frappe ou à des mauvaises configurations dans les modules qui dépendent d'effets secondaires globaux, garantissant que tous les imports sont résolus explicitement. Il s'agit d'une vérification robuste qui, une fois activée, rend votre graphe d'importation plus fiable.
Optimisations des performances et du système de build
L'équipe TypeScript a maintenu un fort accent sur les performances du compilateur, et la série 5.x poursuit cette tendance, offrant des améliorations tangibles pour les temps de build et les cycles de développement.
Optimisations des performances et de la taille du compilateur (dans l'ensemble de 5.x, en particulier 5.5)
TypeScript 5.0 a jeté des bases importantes en passant des espaces de noms aux modules, ce qui a entraîné des améliorations significatives des temps de build (jusqu'à 81 % dans certains projets) et une réduction de la taille du package. TypeScript 5.5 a poursuivi cette trajectoire avec d'autres "Optimisations des performances et de la taille", notamment la vérification ignorée dans transpileModule et les optimisations de la manière dont les types contextuels sont filtrés. Ces optimisations contribuent à des temps de build et d'itération plus rapides dans de nombreux scénarios courants. L'effort constant ici est crucial pour les grandes bases de code, où même de faibles pourcentages de gains se traduisent par des économies de temps importantes sur une journée de développement.
L'option --noCheck (TypeScript 5.6)
TypeScript 5.6 introduit l'option de compilateur --noCheck, qui vous permet de sauter la vérification de type pour tous les fichiers d'entrée. Ce n'est pas une recommandation pour une utilisation générale, mais c'est un outil pragmatique pour des scénarios spécifiques, tels que la séparation de la génération de fichiers JavaScript de la vérification de type. Par exemple, vous pouvez exécuter tsc --noCheck pour une sortie JavaScript rapide pendant l'itération du développement, puis tsc --noEmit comme une phase de vérification de type distincte et approfondie dans CI. Cette séparation peut accélérer considérablement la génération initiale des artefacts de build, mais elle nécessite une intégration prudente dans votre pipeline de build pour garantir que la sécurité des types n'est pas compromise en aval.
--build avec les erreurs intermédiaires (TypeScript 5.6)
Dans les versions précédentes, l'utilisation du mode --build interrompait l'ensemble du processus de build si des erreurs étaient rencontrées dans les dépendances en amont. TypeScript 5.6 permet désormais au processus de build de continuer même si des erreurs intermédiaires sont présentes, en générant des fichiers de sortie sur la base d'un effort maximal. Il s'agit d'une amélioration pratique pour les monorepos ou lors de refactorisations à grande échelle où les dépendances peuvent être mises à niveau de manière incrémentale, empêchant un seul package cassé de bloquer l'ensemble du flux de développement. Pour les environnements où une défaillance stricte est préférée (par exemple, CI), le nouveau drapeau --stopOnBuildErrors peut être utilisé pour revenir au comportement plus strict.
Prise en charge de la mise en cache de la compilation V8 dans Node.js (aperçu de TypeScript 5.7)
En regardant un peu plus loin, TypeScript 5.7, dont la sortie est prévue prochainement, exploite l'API module.enableCompileCache() de Node.js 22. Cela permet à l'environnement d'exécution Node.js de réutiliser le travail d'analyse et de compilation, ce qui entraîne une exécution plus rapide des outils TypeScript. Les tests de l'équipe TypeScript ont montré des résultats impressionnants, avec tsc --version s'exécutant environ 2,5 fois plus rapidement avec la mise en cache activée. Bien que les temps de build spécifiques au projet varient, cette amélioration fondamentale de l'interaction sous-jacente avec l'environnement d'exécution promet des accélérations notables pour tout outil TypeScript basé sur Node.js.
Vérification de la réalité et perspectives
La série 5.x a été une période d'amélioration solide et itérative pour TypeScript. L'accent mis sur l'amélioration de la précision du système de types, le perfectionnement des outils de développement et le renforcement des performances du compilateur démontre une approche pragmatique de l'évolution du langage. Bien que des fonctionnalités telles que les prédicats de type inférés et la restriction préservée simplifient les modèles courants, et que --noCheck offre une flexibilité tactique, il est essentiel de reconnaître que certaines des vérifications plus strictes (par exemple, les vérifications nullish/truthy interdites) peuvent faire surface des problèmes logiques existants dans les bases de code plus anciennes. Il ne s'agit pas de fonctionnalités "cassées" ; ce sont des resserrements intentionnels qui améliorent la robustesse à long terme de votre code, mais ils nécessitent un effort de migration.
La documentation de ces nouvelles fonctionnalités est généralement robuste, en particulier sur le blog officiel de TypeScript. Cependant, comme pour tout outil en évolution rapide, certaines des interactions plus nuancées, en particulier avec les génériques complexes ou les options de compilation avancées, peuvent nécessiter une recherche dans les problèmes GitHub ou les discussions communautaires.
Par rapport aux itérations majeures précédentes, les versions 5.x semblent moins axées sur l'introduction de paradigmes entièrement nouveaux et plus sur le perfectionnement de ceux qui existent déjà, la résolution de points sensibles de longue date et la garantie que TypeScript reste une base solide et efficace pour le développement JavaScript moderne. L'alignement continu sur les normes ECMAScript et la poursuite incessante des gains de performances signifient que chaque mise à jour apporte des avantages tangibles, ce qui fait de l'investissement dans la mise à niveau une entreprise constamment gratifiante.
Sources
🛠️ Outils connexes
Explorez ces outils DataFormatHub liés à ce sujet :
- Formateur de code - Formater le code TypeScript
- JSON vers YAML - Convertir tsconfig entre les formats
