
Microsoft vient de publier la version b�ta de TypeScript 5.8. Cette version am�liore la fa�on dont les types de retour sont v�rifi�s pour les types d'acc�s conditionnels et index�s, optimise l'interop�rabilit� des modules dans Node.js, et inclut des am�liorations de performance pour une r�solution plus rapide des projets.
Pour commencer � utiliser la version b�ta, vous pouvez l'obtenir via npm avec la commande suivante :
Code : | S�lectionner tout |
npm install -D typescript@beta
Retours v�rifi�s pour les types d'acc�s conditionnels et index�s
Consid�rons une API qui pr�sente un ensemble d'options � un utilisateur :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /** * @param prompt The text to show to a user. * @param selectionKind Whether a user can select multiple options, or just a single option. * @param items Each of the options presented to the user. **/ async function showQuickPick( prompt: string, selectionKind: SelectionKind, items: readonly string[], ): Promise<string | string[]> { // ... } enum SelectionKind { Single, Multiple, } |
L'objectif de showQuickPick est d'afficher un �l�ment d'interface utilisateur qui permet de s�lectionner une ou plusieurs options. Le moment o� il le fait est d�termin� par le param�tre selectionKind. Lorsque selectionKind est SelectionKind.Single, le type de retour de showQuickPick doit �tre string, et lorsqu'il est SelectionKind.Multiple, le type de retour doit �tre string[].
Le probl�me est que la signature de type de showQuickPick n'est pas claire. Elle indique simplement que le type retourn� est string | string[] - il pourrait s'agir d'un string ou d'une string[], mais l'appelant doit v�rifier explicitement. Dans l'exemple ci-dessous, on pourrait s'attendre � ce que shoppingList ait le type string[], mais on se retrouve avec le type plus large string | string[].
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | let shoppingList = await showQuickPick( "Which fruits do you want to purchase?", SelectionKind.Multiple, ["apples", "oranges", "bananas", "durian"], ); console.log(`Alright, going out to buy some ${shoppingList.join(", ")}`); // ~~~~ // error! // Property 'join' does not exist on type 'string | string[]'. // Property 'join' does not exist on type 'string'. |
Au lieu de cela, nous pouvons utiliser un type conditionnel pour rendre le type de retour de showQuickPick plus pr�cis :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 | type QuickPickReturn<S extends SelectionKind> = S extends SelectionKind.Multiple ? string[] : string async function showQuickPick<S extends SelectionKind>( prompt: string, selectionKind: S, items: readonly string[], ): Promise<QuickPickReturn<S>> { // ... } |
Cela fonctionne bien pour les appelants.
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | // `SelectionKind.Multiple` gives a `string[]` - works let shoppingList: string[] = await showQuickPick( "Which fruits do you want to purchase?", SelectionKind.Multiple, ["apples", "oranges", "bananas", "durian"], ); // `SelectionKind.Single` gives a `string` - works let dinner: string = await showQuickPick( "What's for dinner tonight?", SelectionKind.Single, ["sushi", "pasta", "tacos", "ugh I'm too hungry to think, whatever you want"], ); |
Mais qu'en est-il si nous essayons d'impl�menter showQuickPick ?
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | async function showQuickPick<S extends SelectionKind>( prompt: string, selectionKind: S, items: readonly string[], ): Promise<QuickPickReturn<S>> { if (items.length < 1) { throw new Error("At least one item must be provided."); } // Create buttons for every option. let buttons = items.map(item => ({ selected: false, text: item, })); // Default to the first element if necessary. if (selectionKind === SelectionKind.Single) { buttons[0].selected = true; } // Event handling code goes here... // Figure out the selected items const selectedItems = buttons .filter(button => button.selected) .map(button => button.text); if (selectionKind === SelectionKind.Single) { // Pick the first (only) selected item. return selectedItems[0]; } else { // Return all selected items. return selectedItems; } } |
Malheureusement, TypeScript �met une erreur sur chacune des instructions de retour.
Code : | S�lectionner tout |
1 2 | Type 'string[]' is not assignable to type 'QuickPickReturn<S>'. Type 'string' is not assignable to type 'QuickPickReturn<S>'. |
Jusqu'� pr�sent, TypeScript exigeait une assertion de type pour impl�menter toute fonction renvoyant un type conditionnel d'ordre sup�rieur.
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 | if (selectionKind === SelectionKind.Single) { // Pick the first (only) selected item. - return selectedItems[0]; + return selectedItems[0] as QuickPickReturn<S>; } else { // Return all selected items. - return selectedItems; + return selectedItems as QuickPickReturn<S>; } |
Cette situation n'est pas id�ale car les assertions de type annulent les v�rifications l�gitimes que TypeScript effectuerait autrement. Par exemple, il serait id�al que TypeScript puisse d�tecter le bogue suivant o� chaque branche du if/else est m�lang�e :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 | if (selectionKind === SelectionKind.Single) { // Oops! Returning an array when the caller expects a single item! return selectedItems; } else { // Oops! Returning a single item when the caller expects an array! return selectedItems[0]; } |
Pour �viter les assertions de type, TypeScript 5.8 prend d�sormais en charge une forme limit�e de v�rification des types conditionnels dans les instructions de retour. Lorsque le type de retour d'une fonction est un type conditionnel g�n�rique, TypeScript utilise d�sormais l'analyse du flux de contr�le pour les param�tres g�n�riques dont les types sont utilis�s dans le type conditionnel, instancie le type conditionnel avec le type r�duit de chaque param�tre, et effectue une v�rification par rapport � ce nouveau type.
Qu'est-ce que cela signifie en pratique ? Tout d'abord, examinons quels genres de types conditionnels impliquent une r�duction. Pour refl�ter la fa�on dont la r�duction op�re dans les expressions, nous devons �tre plus explicites et exhaustifs sur ce qui se passe dans chaque branche.
Code : | S�lectionner tout |
1 2 3 4 | type QuickPickReturn<S extends SelectionKind> = S extends SelectionKind.Multiple ? string[] : S extends SelectionKind.Single ? string : never; |
Une fois que c'est fait, tout fonctionne dans l'exemple que nous venons de donner. Les appelants n'ont aucun probl�me, et l'impl�mentation est maintenant s�re. Et si l'on essaie d'intervertir le contenu des branches du if, TypeScript le signale correctement comme une erreur.
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | if (selectionKind === SelectionKind.Single) { // Oops! Returning an array when the caller expects a single item! return selectedItems; // ~~~~~~ // error! Type 'string[]' is not assignable to type 'string'. } else { // Oops! Returning a single item when the caller expects an array! return selectedItems[0]; // ~~~~~~ // error! Type 'string[]' is not assignable to type 'string'. } |
Notez que TypeScript fait d�sormais quelque chose de similaire si l'on utilise des types d'acc�s index�s. Au lieu d'un type conditionnel, il est possible d'utiliser un type qui agit comme un plan de SelectionKind vers le type de retour que l'on souhaite :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | interface QuickPickReturn { [SelectionKind.Single]: string; [SelectionKind.Multiple]: string[]; } async function showQuickPick<S extends SelectionKind>( prompt: string, selectionKind: S, items: readonly string[], ): Promise<QuickPickReturn[S]> { // ... } |
Pour de nombreux utilisateurs, il s'agira d'une mani�re plus ergonomique d'�crire le m�me code. Toutefois, cette fonctionnalit� pr�sente certaines limitations.
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 utiliser import sur des fichiers CommonJS
- Les fichiers CommonJS ne pouvaient pas utiliser require() pour 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 un await de premier niveau, mais la plupart des autres fichiers ESM peuvent d�sormais �tre consomm�s � partir de 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 permette ce comportement ; cependant, il est pr�vu que les futures versions de TypeScript puissent stabiliser cette 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 un flag 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 import) 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 runtime
- propri�t�s des param�tres dans les classes
- alias import
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 vous ne d�couvrirez toujours pas que votre code ne fonctionne pas tant que vous n'aurez pas essay� de l'ex�cuter.
C'est pourquoi TypeScript 5.8 introduit l'option --erasableSyntaxOnly. Lorsque ce drapeau est activ�, TypeScript ne vous permet d'utiliser que des constructions qui peuvent �tre effac�es d'un fichier, et �met une erreur s'il rencontre des constructions qui ne peuvent pas �tre effac�es.
Code : | S�lectionner tout |
1 2 3 4 5 6 | class C { constructor(public x: number) { } // ~~~~~~~~~~~~~~~~ // error! This syntax is not allowed when 'erasableSyntaxOnly' is enabled. } } |
Le flag --libReplacement
Dans TypeScript 4.5, la possibilit� de remplacer les fichiers lib par d�faut par des fichiers personnalis�s a �t� introduite. 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 :
Code : | S�lectionner tout |
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 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 :
Code : | S�lectionner tout |
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.
Code : | S�lectionner tout |
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 :
Code : | S�lectionner tout |
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 des unique symbol ou des types litt�raux.
Notez que l'�criture de ce code �tait et est toujours une erreur sous le flag --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�trocompatible 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
L'ensemble des changements de comportement notables qu'il convient de reconna�tre et de comprendre dans le cadre de la mise � jour sont disponibles ici.
Quelles sont les prochaines �tapes ?
� ce stade, TypeScript 5.8 est � feature-stable �. TypeScript 5.8 se concentrera sur les corrections de bogues, le polissage et certaines fonctionnalit�s � faible risque de l'�diteur. Une version candidate sera disponible dans les prochaines semaines, suivie d'une version stable peu apr�s.
Source : Microsoft
Et vous ?

Voir aussi :



Vous avez lu gratuitement 0 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer � vous proposer des publications.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer � vous proposer des publications.