Microsoft annonce la disponibilité de la version stable de TypeScript 5.7. Cette version introduit une meilleure vérification des variables non initialisées, une nouvelle option pour la réécriture de chemin pour les chemins relatifs, ainsi que la prise en charge de --target es2024 et --lib es2024. Cette version apporte également des améliorations à la recherche de fichiers de configuration ancestraux pour la propriété du projet et aux vérifications de la propriété du projet dans les éditeurs pour les projets composites. Pour ceux qui ne connaissent pas, TypeScript est un langage qui s'appuie sur JavaScript en ajoutant une syntaxe pour les déclarations de type et les annotations. Cette syntaxe peut être utilisée par le compilateur TypeScript pour vérifier les types du code, et elle peut également être effacée pour émettre un code JavaScript propre et idiomatique.
Le contrôle de type est utile parce qu'il permet de détecter à l'avance les bogues dans le code, mais l'ajout de types au code le rend également plus lisible et permet à des outils comme les éditeurs de code d'offrir des fonctionnalités puissantes comme l'auto-complétion, les refactorisations, la recherche de toutes les références, et bien d'autres encore. TypeScript est un surensemble de JavaScript, de sorte que tout code JavaScript valide est également un code TypeScript valide.
Après la sortie de la version candidate au début du mois, Microsoft vient d'annoncer la disponibilité de la version stable de TypeScript 5.7. Pour commencer à utiliser TypeScript, il est possible de l'obtenir via npm à l'aide de la commande suivante :
| Code : | Sélectionner tout |
npm install -D typescript
Voici un aperçu des nouveautés de TypeScript 5.7.
Vérification des variables non initialisées
Depuis longtemps, TypeScript est capable de détecter les problèmes lorsqu'une variable n'a pas encore été initialisée dans toutes les branches précédentes.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | let result: number
if (someCondition()) {
result = doSomeWork();
}
else {
let temporaryWork = doSomeWork();
temporaryWork *= 2;
// forgot to assign to 'result'
}
console.log(result); // error: Variable 'result' is used before being assigned. |
Malheureusement, il y a des endroits où cette analyse ne fonctionne pas. Par exemple, si la variable est accédée dans une fonction séparée, le système de type ne sait pas quand la fonction sera appelée, et prend une vue « optimiste » que la variable sera initialisée.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function foo() {
let result: number
if (someCondition()) {
result = doSomeWork();
}
else {
let temporaryWork = doSomeWork();
temporaryWork *= 2;
// forgot to assign to 'result'
}
printResult();
function printResult() {
console.log(result); // no error here.
}
} |
Bien que TypeScript 5.7 soit encore indulgent avec les variables qui ont été éventuellement initialisées, le système de types est capable de signaler des erreurs lorsque les variables n'ont jamais été initialisées du tout.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | function foo() {
let result: number
// do work, but forget to assign to 'result'
function printResult() {
console.log(result); // error: Variable 'result' is used before being assigned.
}
} |
Réécriture de chemin pour les chemins relatifs
Il existe plusieurs outils et moteurs d'exécution qui permettent d'exécuter du code TypeScript « in-place », ce qui signifie qu'ils ne nécessitent pas d'étape de construction qui génère des fichiers JavaScript de sortie. Par exemple, ts-node, tsx, Deno et Bun permettent tous d'exécuter des fichiers .ts directement. Plus récemment, Node.js a étudié ce support avec --experimental-transform-types et --experimental-strip-types. C'est extrêmement pratique car cela permet d'itérer plus rapidement sans avoir à se soucier de relancer une tâche de construction.
Il y a cependant une certaine complexité à prendre en compte lors de l'utilisation de ces modes. Pour être compatible avec tous ces outils, un fichier TypeScript importé « in-place » doit être importé avec l'extension TypeScript appropriée au moment de l'exécution. Par exemple, pour importer un fichier appelé foo.ts, il faut écrire ce qui suit dans le nouveau support expérimental de Node :
| Code : | Sélectionner tout |
1 2 3 | // main.ts import * as foo from "./foo.ts"; // <- we need foo.ts here, not foo.js |
Typiquement, TypeScript émettrait une erreur si on le faisait, parce qu'il s'attend à ce qu'on importe le fichier de sortie. Parce que certains outils autorisent les importations .ts, TypeScript a supporté ce style d'importation avec une option appelée --allowImportingTsExtensions depuis un certain temps. Cela fonctionne bien, mais que se passe-t-il si on a besoin de générer des fichiers .js à partir de ces fichiers .ts ? C'est une exigence pour les auteurs de bibliothèques qui devront être en mesure de distribuer uniquement des fichiers .js, mais jusqu'à présent TypeScript a évité de réécrire les chemins.
Pour prendre en charge ce scénario, une nouvelle option du compilateur a été ajoutée : --rewriteRelativeImportExtensions. Lorsqu'un chemin d'importation est relatif (commence par ./ ou ../), se termine par une extension TypeScript (.ts, .tsx, .mts, .cts), et qu'il s'agit d'un fichier sans déclaration, le compilateur réécrira le chemin vers l'extension JavaScript correspondante (.js, .jsx, .mjs, .cjs).
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | // Under --rewriteRelativeImportExtensions... // these will be rewritten. import * as foo from "./foo.ts"; import * as bar from "../someFolder/bar.mts"; // these will NOT be rewritten in any way. import * as a from "./foo"; import * as b from "some-package/file.ts"; import * as c from "@some-scope/some-package/file.ts"; import * as d from "#/file.ts"; import * as e from "./file.js"; |
Cela permet d'écrire du code TypeScript qui peut être exécuté sur place, puis compilé en JavaScript lorsque tout est prêt.
Maintenant, il a été noté que TypeScript évitait en général de réécrire les chemins d'accès. Il y a plusieurs raisons à cela, mais la plus évidente est celle des importations dynamiques. Si un développeur écrit ce qui suit, il n'est pas trivial de gérer le chemin que l'import reçoit. En fait, il est impossible de remplacer le comportement de l'import dans les dépendances.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | function getPath() {
if (Math.random() < 0.5) {
return "./foo.ts";
}
else {
return "./foo.js";
}
}
let myImport = await import(getPath()); |
Un autre problème est que (comme cela a été vu plus haut) seuls les chemins relatifs sont réécrits, et ils sont écrits de manière "naïve". Cela signifie que tout chemin qui repose sur la baseUrl et les paths de TypeScript ne sera pas réécrit :
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | // tsconfig.json
{
"compilerOptions": {
"module": "nodenext",
// ...
"paths": {
"@/*": ["./src/*"]
}
}
} |
| Code : | Sélectionner tout |
1 2 | // Won't be transformed, won't work. import * as utilities from "@/utilities.ts"; |
Il en va de même pour tout chemin qui pourrait être résolu par les champs exports et imports d'un package.json.
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 | // package.json
{
"name": "my-package",
"imports": {
"#root/*": "./dist/*"
}
} |
| Code : | Sélectionner tout |
1 2 | // Won't be transformed, won't work. import * as utilities from "#root/utilities.ts"; |
Par conséquent, si une présentation de type espace de travail est utilisée avec plusieurs paquets se référençant les uns les autres, il se peut que l'utilisation des exportations conditionnelles avec des conditions personnalisées étendues soit nécessaire pour que cela fonctionne :
| Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // my-package/package.json
{
"name": "my-package",
"exports": {
".": {
"@my-package/development": "./src/index.ts",
"import": "./lib/index.js"
},
"./*": {
"@my-package/development": "./src/*.ts",
"import": "./lib/*.js"
}
}
} |
La fin de cet article est réservée aux abonnés. Soutenez le Club Developpez.com en prenant un abonnement pour que nous puissions continuer à vous proposer des publications.