Microsoft annonce la disponibilité de TypeScript 5.8, apportant des vérifications plus granulaires pour les branches dans les expressions de retour, ainsi que plusieurs autres nouveautés et améliorations
Microsoft annonce la disponibilité générale (GA) de TypeScript 5.8. Cette version apporte plusieurs nouveautés et améliorations, cependant, une nouvelle fonctionnalité introduite lors de la phase bêta, une forme limitée de vérification des types conditionnels dans les instructions de retour, a été supprimée de la version GA.
Cette annonce intervient après la publication de la release candidate de TypeScript 5.8, qui apporte des contrôles plus granulaires pour les branches à l'intérieur des expressions de retour. Avec ces contrôles granulaires, le système de type traite les expressions conditionnelles directement à l'intérieur des instructions de retour. Chaque branche de l'expression conditionnelle est ainsi vérifiée par rapport au type de retour déclaré de la fonction qui la contient, s'il en existe un.
TypeScript est un langage qui s'appuie sur JavaScript en ajoutant une syntaxe pour les types. L'écriture de types dans le code permet d'expliquer l'intention et de faire vérifier le code par d'autres outils pour détecter les erreurs comme les fautes de frappe, les problèmes avec null et undefined, et plus encore. Les types alimentent également les outils d'édition de TypeScript, comme l'auto-complétion, la navigation dans le code et les refactorisations que vous pouvez voir dans des éditeurs tels que Visual Studio et VS Code. En fait, TypeScript et son écosystème alimentent l'expérience JavaScript dans ces deux éditeurs également.
Pour commencer à utiliser TypeScript 5.8, vous pouvez l'installer via npm avec la commande suivante :
npm install -D typescript
Jetons un coup d'œil aux nouveautés de TypeScript 5.8.
Quelles sont les nouveautés depuis la bêta et la RC ?
Depuis la sortie de la version bêta, Microsoft a dû faire marche arrière sur la façon dont les fonctions avec des types de retour conditionnels sont vérifiées. Sur la base de certaines limitations et modifications que Microsoft souhaitait apporter, l'équipe a décidé d'itérer sur la fonctionnalité dans le but de la livrer dans TypeScript 5.9. Cependant, dans le cadre de ce travail, Microsoft a ajouté des vérifications plus granulaires pour les branches dans les expressions de retour.
Aucun nouveau changement majeur n'a été ajouté depuis la version candidate.
Vérifications granulaires des branches dans les expressions de retour
Considérons un code comme le suivant :
1 2 3 4 5 6 7
| declare const untypedCache: Map<any, any>;
function getUrlObject(urlString: string): URL {
return untypedCache.has(urlString) ?
untypedCache.get(urlString) :
urlString;
} |
L'intention de ce code est de récupérer un objet URL à partir d'un cache s'il existe, ou de créer un nouvel objet URL s'il n'existe pas. Cependant, il y a un bug : on a oublié de construire un nouvel objet URL avec l'entrée. Malheureusement, TypeScript ne détecte généralement pas ce genre de bogue.
Lorsque TypeScript vérifie des expressions conditionnelles comme cond ? trueBranch : falseBranch, son type est traité comme une union des types des deux branches. En d'autres termes, il obtient le type de trueBranch et de falseBranch, et les combine en un type d'union. Dans ce cas, le type de untypedCache.get(urlString) est any, et le type de urlString est string. C'est là que les choses se gâtent, car le type any est tellement infectieux dans la manière dont il interagit avec les autres types. L'union any | string est simplifiée en any, de sorte qu'au moment où TypeScript commence à vérifier si l'expression de l'instruction return est compatible avec le type de retour attendu d'URL, le système de types a perdu toute information qui aurait permis de détecter le bogue dans ce code.
Dans TypeScript 5.8, le système de type traite les expressions conditionnelles directement à l'intérieur des instructions return. Chaque branche de l'expression conditionnelle est vérifiée par rapport au type de retour déclaré de la fonction qui la contient (s'il y en a un), de sorte que le système de type peut détecter le bogue dans l'exemple ci-dessus.
1 2 3 4 5 6 7 8 9
| declare const untypedCache: Map<any, any>;
function getUrlObject(urlString: string): URL {
return untypedCache.has(urlString) ?
untypedCache.get(urlString) :
urlString;
// ~~~~~~~~~
// error! Type 'string' is not assignable to type 'URL'.
} |
Prise en charge de require() pour les modules ECMAScript dans --module nodenext
Pendant des années, Node.js a supporté les modules ECMAScript (ESM) en même temps que les modules CommonJS. Malheureusement, l'interopérabilité entre les deux a posé quelques problèmes.
- Les fichiers ESM pouvaient import des fichiers CommonJS
- Les fichiers CommonJS ne pouvaient pas require() les fichiers ESM
En d'autres termes, il était possible de consommer des fichiers CommonJS à partir de fichiers ESM, mais pas l'inverse. Cela a posé de nombreux problèmes aux auteurs de bibliothèques qui souhaitaient fournir un support ESM. Ces auteurs devaient soit rompre la compatibilité avec les utilisateurs de CommonJS, soit « publier deux fois » leurs bibliothèques (en fournissant des points d'entrée distincts pour ESM et CommonJS), soit rester indéfiniment sur CommonJS. Bien que la double publication puisse sembler être un bon compromis, il s'agit d'un processus complexe et sujet aux erreurs, qui double également la quantité de code dans un paquetage.
Node.js 22 assouplit certaines de ces restrictions et autorise les appels require(« esm ») des modules CommonJS vers les modules ECMAScript. Node.js n'autorise toujours pas l'utilisation de require() pour les fichiers ESM qui contiennent une await de premier niveau, mais la plupart des autres fichiers ESM sont désormais consommables à partir des fichiers CommonJS. Cela représente une opportunité majeure pour les auteurs de bibliothèques de fournir un support ESM sans avoir à publier leurs bibliothèques en double.
TypeScript 5.8 supporte ce comportement avec l'option --module nodenext. Lorsque --module nodenext est activé, TypeScript évitera d'émettre des erreurs sur ces appels require() aux fichiers ESM.
Parce que cette fonctionnalité peut être rétroportée vers des versions plus anciennes de Node.js, il n'y a actuellement pas d'option stable --module nodeXXXX qui active ce comportement ; cependant, Microsoft prédit que les futures versions de TypeScript pourront stabiliser la fonctionnalité sous node20. En attendant, les utilisateurs de Node.js 22 et plus récents sont encouragés à utiliser --module nodenext, tandis que les auteurs de bibliothèques et les utilisateurs de versions plus anciennes de Node.js devraient continuer à utiliser --module node16 (ou faire la mise à jour mineure vers --module node18).
--module node18
TypeScript 5.8 introduit une option stable --module node18. Pour les utilisateurs qui sont fixés sur l'utilisation de Node.js 18, ce drapeau fournit un point de référence stable qui n'incorpore pas certains comportements qui sont dans --module nodenext. En particulier :
- require() des modules ECMAScript est interdit sous node18, mais autorisé sous nodenext
- les assertions import (dépréciées en faveur des attributs d'importation) sont autorisées sous node18, mais ne sont pas autorisées sous nodenext
L'option --erasableSyntaxOnly
Récemment, Node.js 23.6 a supprimé le support expérimental pour l'exécution directe de fichiers TypeScript ; cependant, seules certaines constructions sont supportées dans ce mode. Node.js a supprimé un mode appelé --experimental-strip-types qui exige que toute syntaxe spécifique à TypeScript ne puisse pas avoir de sémantique d'exécution. En d'autres termes, il doit être possible d'effacer facilement toute syntaxe spécifique à TypeScript d'un fichier, en laissant un fichier JavaScript valide.
Cela signifie que des constructions comme celles qui suivent ne sont pas prises en charge :
- déclarations enum
- namespaces et modules avec code d'exécution
- propriétés des paramètres dans les classes
- alias import =
Voici quelques exemples de ce qui ne fonctionne pas :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| // error: A namespace with runtime code.
namespace container {
foo.method();
export type Bar = string;
}
// error: An `import =` alias
import Bar = container.Bar;
class Point {
// error: Parameter properties
constructor(public x: number, public y: number) { }
}
// error: An enum declaration.
enum Direction {
Up,
Down,
Left,
Right,
} |
Des outils similaires comme ts-blank-space ou Amaro (la bibliothèque sous-jacente pour la séparation des types dans Node.js) ont les mêmes limitations. Ces outils fourniront des messages d'erreur utiles s'ils rencontrent du code qui ne répond pas à ces exigences, mais l'utilisateur ne découvrira toujours pas que son code ne fonctionne pas tant qu'il n'aura pas essayé de l'exécuter.
C'est pourquoi TypeScript 5.8 introduit l'option --erasableSyntaxOnly. Lorsque ce drapeau est activé, TypeScript génère des erreurs sur la plupart des constructions spécifiques à TypeScript qui ont un comportement à l'exécution.
1 2 3 4 5 6
| class C {
constructor(public x: number) { }
// ~~~~~~~~~~~~~~~~
// error! This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
}
} |
Typiquement, vous voudrez combiner cette option avec --verbatimModuleSyntax, qui garantit qu'un module contient la syntaxe d'importation appropriée, et que l'élision d'importation n'a pas lieu.
L'option --libReplacement
Dans TypeScript 4.5, Microsoft a introduit la possibilité de remplacer les fichiers lib par défaut par des fichiers personnalisés. Ceci était basé sur la possibilité de résoudre un fichier de bibliothèque à partir de paquets nommés @typescript/lib-*. Par exemple, vous pouvez verrouiller vos bibliothèques dom sur une version spécifique du package @types/web avec le package.json suivant :
1 2 3 4 5
| {
"devDependencies": {
"@typescript/lib-dom": "npm:@types/web@0.0.199"
}
} |
Une fois installé, un paquetage appelé @typescript/lib-dom devrait exister, et TypeScript le recherchera toujours lorsque dom est impliqué par vos paramètres.
Il s'agit d'une fonctionnalité puissante, mais qui implique également un peu de travail supplémentaire. Même si vous n'utilisez pas cette fonctionnalité, TypeScript effectue toujours cette recherche et doit surveiller les changements dans node_modules au cas où un paquetage de remplacement de lib commencerait à exister.
TypeScript 5.8 introduit l'option --libReplacement, qui permet de désactiver ce comportement. Si vous n'utilisez pas --libReplacement, vous pouvez maintenant le désactiver avec --libReplacement false. Dans le futur, --libReplacement false pourrait devenir la valeur par défaut, donc si vous utilisez actuellement ce comportement, vous devriez envisager de l'activer explicitement avec --libReplacement true.
Conservation des noms de propriété calculés dans les fichiers de déclaration
Afin de rendre l'émission des propriétés calculées plus prévisible dans les fichiers de déclaration, TypeScript 5.8 préservera systématiquement les noms d'entités (bareVariables et dotted.names.that.look.like.this) dans les noms de propriétés calculées dans les classes.
Par exemple, considérons le code suivant :
1 2 3 4 5 6 7 8
| export let propName = "theAnswer";
export class MyClass {
[propName] = 42;
// ~~~~~~~~~~
// error!
// A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type.
} |
Les versions précédentes de TypeScript émettaient une erreur lors de la génération d'un fichier de déclaration pour ce module, et un fichier de déclaration au mieux de sa forme générait une signature d'index.
1 2 3 4
| export declare let propName: string;
export declare class MyClass {
[x: string]: number;
} |
Dans TypeScript 5.8, l'exemple de code est désormais autorisé et le fichier de déclaration généré correspondra à ce que vous avez écrit :
1 2 3 4
| export declare let propName: string;
export declare class MyClass {
[propName]: number;
} |
Notez que cela ne crée pas de propriétés nommées statiquement sur la classe. Vous obtiendrez toujours ce qui est en fait une signature d'index comme [x : string] : number, donc pour ce cas d'utilisation, vous devrez utiliser unique symbol ou des types littéraux.
Notez que l'écriture de ce code était et est toujours une erreur sous l'option --isolatedDeclarations ; mais il est prévu que grâce à ce changement, les noms de propriétés calculés seront généralement autorisés dans les déclarations emit.
Il est possible (bien que peu probable) qu'un fichier compilé en TypeScript 5.8 génère un fichier de déclaration qui n'est pas rétro-compatible avec TypeScript 5.7 ou antérieur.
Optimisation du chargement et de la mise à jour des programmes
TypeScript 5.8 introduit un certain nombre d'optimisations qui peuvent à la fois améliorer le temps de construction d'un programme, et également mettre à jour un programme basé sur un changement de fichier, que ce soit en mode --watch ou dans les scénarios de l'éditeur.
Tout d'abord, TypeScript évite désormais les allocations de tableaux qui seraient nécessaires lors de la normalisation des chemins. Typiquement, la normalisation des chemins implique de segmenter chaque portion d'un chemin en un tableau de chaînes de caractères, de normaliser le chemin résultant sur la base des segments relatifs, puis de les réunir à l'aide d'un séparateur canonique. Pour les projets comportant de nombreux fichiers, cela peut représenter un travail important et répétitif. TypeScript évite désormais d'allouer un tableau et opère plus directement sur les index du chemin d'origine.
De plus, lorsque des modifications sont apportées sans changer la structure fondamentale d'un projet, TypeScript évite désormais de revalider les options qui lui sont fournies (par exemple, le contenu d'un tsconfig.json). Cela signifie, par exemple, qu'une simple modification peut ne pas nécessiter de vérifier que les chemins de sortie d'un projet n'entrent pas en conflit avec les chemins d'entrée. Au lieu de cela, les résultats de la dernière vérification peuvent être utilisés. Cela devrait permettre aux éditeurs de grands projets d'être plus réactifs.
Changements de comportement notables
Cette section présente un ensemble de changements notables qu'il convient de reconnaître et de comprendre dans le cadre d'une mise à jour. Elle met parfois en évidence des dépréciations, des suppressions et de nouvelles restrictions. Elle peut également contenir des corrections de bogues qui sont des améliorations fonctionnelles, mais qui peuvent également affecter une version existante en introduisant de nouvelles erreurs.
lib.d.ts
Les types générés pour le DOM peuvent avoir un impact sur la vérification de type de votre base de code. Pour plus d'informations, voir les problèmes liés aux mises à jour de DOM et lib.d.ts pour cette version de TypeScript.
Restrictions sur les assertions d'importation sous --module nodenext
Les assertions d'importation étaient un ajout proposé à ECMAScript pour garantir certaines propriétés d'une importation (par exemple, « ce module est JSON, et n'est pas destiné à être du code JavaScript exécutable »). Ils ont été réinventés sous la forme d'une proposition appelée « import attributes » (attributs d'importation). Dans le cadre de la transition, ils sont passés du mot-clé assert au mot-clé with.
1 2 3 4 5
| // An import assertion - not future-compatible with most runtimes.
import data from "./data.json" assert { type: "json" };
// An import attribute - the preferred way to import a JSON file.
import data from "./data.json" with { type: "json" }; |
Node.js 22 n'accepte plus les assertions d'importation utilisant la syntaxe assert. Par ailleurs, lorsque l'option --module nodenext est activée dans TypeScript 5.8, TypeScript émettra une erreur s'il rencontre une assertion d'importation.
1 2 3
| import data from "./data.json" assert { type: "json" };
// ~~~~~~
// error! Import assertions have been replaced by import attributes. Use 'with' instead of 'assert' |
Quelle sera la suite ?
La prochaine version de TypeScript sera TypeScript 5.9, et les développeurs auront plus de détails grâce à un plan d'itération à venir sur l'issue tracker de Microsoft. Ce plan contiendra des dates précises et plus de détails sur les fonctionnalités à venir sur lesquelles l'équipe de TypeScript prévoit de travailler. En attendant, vous pouvez essayer les premières versions de TypeScript 5.9 dès aujourd'hui en installant les nightly builds depuis npm avec npm install typescript@next, ou en utilisant l'extension VS Code TypeScript Nightly.
Microsoft conclut : « En attendant, TypeScript 5.8 est là et prêt à l'emploi, et nous sommes impatients que vous l'installiez dès aujourd'hui ! Nous espérons que cette version rendra votre travail de codage quotidien plus agréable. »
Source : Microsoft
Et vous ?
Quel est votre avis sur TypeScript 5.8 ?
Quelles sont les fonctionnalités que vous aimeriez retrouver dans les futures versions de TypeScript ?
Voir aussi :
Microsoft annonce la Release Candidate de TypeScript 5.8, et apporte des vérifications plus granulaires pour les branches dans les expressions de retour
Microsoft annonce la disponibilité de TypeScript 5.8 Beta avec plusieurs améliorations, dont une meilleure vérification des types de retours pour les types d'accès conditionnels et indexés
Microsoft annonce la disponibilité de TypeScript 5.6, apportant des améliorations à la vérification vraie et nulle non autorisée ainsi que des méthodes d'aide pour les itérateurs
Partager