L'équipe TypeScript de Microsoft a annoncé récemment la sortie de la version 1.8 de son compilateur TypeScript en bêta.
Sans qu'il y ait de nouveautés fondamentales, il s'agit davantage d'un ensemble d'améliorations de l'existant qui pourront néanmoins être utiles pour nombre de développeurs. Cela montre en outre que ce langage continue de mûrir tant sur le plan de la syntaxe et de la sémantique que sur le plan de l'outillage et des ponts avec d'autres briques du Web.
Paramètres de types comme contraintes
Tout d'abord, le langage en lui-même s'améliore au niveau de son système de typage. Avec cette nouvelle version, il est désormais possible qu'un type générique fasse référence à un autre type générique d'une même liste (F-Bounded Polymorphism), chose qui n'était pas possible auparavant.
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | function assign<T extends U, U>(target: T, source: U): T { for (let id in source) { target[id] = source[id]; } return target; } let x = { a: 1, b: 2, c: 3, d: 4 }; assign(x, { b: 10, d: 20 }); assign(x, { e: 0 }); // Error |
Types de littéraux
Un ensemble de littéraux peuvent désormais constituer un type à part entière, sans que cela passe par la construction enum, plutôt réservée pour des symboles associés à des nombres.
Cela permet entre autre de détecter des fautes de frappes dans des chaînes de caractères qu'il n'était pas possible de détecter auparavant (et dans d'autres langages aussi).
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 | interface AnimationOptions { deltaX: number; deltaY: number; easing: "ease-in" | "ease-out" | "ease-in-out"; } // Error: Type '"ease-inout"' is not assignable to type '"ease-in" | "ease-out" | "ease-in-out"' new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "ease-inout" }); |
Inférence de type améliorée pour les unions/intersections
L'union et l'intersection de types précédemment introduites dans le langage voient leur inférence de type améliorée.
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 | type Maybe<T> = T | void; function isDefined<T>(x: Maybe<T>): x is T { return x !== undefined && x !== null; } function isUndefined<T>(x: Maybe<T>): x is void { return x === undefined || x === null; } function getOrElse<T>(x: Maybe<T>, defaultValue: T): T { return isDefined(x) ? x : defaultValue; } function test1(x: Maybe<string>) { let x1 = getOrElse(x, "Undefined"); // string let x2 = isDefined(x) ? x : "Undefined"; // string let x3 = isUndefined(x) ? "Undefined" : x; // string } function test2(x: Maybe<number>) { let x1 = getOrElse(x, -1); // number let x2 = isDefined(x) ? x : -1; // number let x3 = isUndefined(x) ? -1 : x; // number } |
Gardes de type basées sur this
Alors que les gardes de type personnalisée pouvaient être des fonctions définies par l'utilisateur, il est dorénavant possible d'en faire à partir de méthodes de classe, autorisant l'utilisation du mot-clé this.
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 | class FileSystemObject { isFile(): this is File { return this instanceof File; } isDirectory(): this is Directory { return this instanceof Directory;} isNetworked(): this is (Networked & this) { return this.networked; } constructor(public path: string, private networked: boolean) {} } class File extends FileSystemObject { constructor(path: string, public content: string) { super(path, false); } } class Directory extends FileSystemObject { children: FileSystemObject[]; } interface Networked { host: string; } let fso: FileSystemObject = new File("foo/bar.txt", "foo"); if (fso.isFile()) { fso.content; // fso is File } else if (fso.isDirectory()) { fso.children; // fso is Directory } else if (fso.isNetworked()) { fso.host; // fso is networked } |
Cela généralise par conséquent la notion de garde de type non seulement aux unions et aux intersections de type, mais également aux classes, faisant de ce concept un élément central dans l'inférence de type dans un contexte de polymorphisme.
Autorisation des let/const capturés dans les boucles
La définition de let ou const dans des boucles pouvaient poser problème lorsque le symbole en question était référencé dans une fonction anonyme fléchée. Avec cette nouvelle version de TypeScript, ce problème a été corrigé.
Code : | Sélectionner tout |
1 2 3 4 5 6 | let list = []; for (let i = 0; i < 5; i++) { list.push(() => i); } list.forEach(f => console.log(f())); |
Code : | Sélectionner tout |
1 2 3 4 5 | 0 1 2 3 4 |
Analyse du flux de contrôle
Jusque-là, en dehors du contrôle sur les types, le compilateur TypeScript ressemblait plus à un simple transpilateur d'un langage vers un autre. Désormais, avec cette version 1.8, le compilateur de Microsoft analyse le programme pour détecter la présence de code inaccessible, de symboles inutilisés, de retours implicites de fonction (--noImplicitReturns), ou l'absence de break à la fin d'un bloc case, sauf si ce bloc case est vide (--noFallthroughCasesInSwitch).
JavaScript dans la compilation TypeScript
On notera la possibilité d'inclure directement dans un projet TypeScript des fichiers JavaScript. Ceci sans avoir à renommer l'extension et sans être pollué par les innombrables messages d'erreurs et d'avertissements lors de la compilation, simplement en utilisant l'option allowJs soit dans la ligne de commande, soit dans le fichier tsconfig.json.
Il est du coup possible de transpiler du JavaScript ES6 en ES5 grâce au compilateur TypeScript.
Code : | Sélectionner tout |
> tsc --allowJs --outDir out target_file.js
Support étendu de ReactJS
TypeScript améliore le support de React en autorisant les composants de fonctions sans état.
Code : | Sélectionner tout |
1 2 3 4 5 | // Use parameter destructuring and defaults for easy definition of 'props' type const Greeter = ({name = 'world'}) => <div>Hello, {name}!</div>; // Properties get validated let example = <Greeter name='TypeScript 1.8' />; |
Modularité
Des améliorations significatives autour des modules ont été réalisées. Par exemple, il est maintenant possible d'étendre un module ou le contexte global à partir d'un autre module, ce qui n'était pas possible auparavant, assez bizarrement d'ailleurs.
Le compilateur TypeScript peut également concaténer des modules AMD et SystemJS.
source : Compte Github officiel de TypeScript
Que pensez-vous de ces nouvelles fonctionnalités ?
Quelle(s) fonctionnalité(s) auriez-vous souhaité en plus de celles-ci ?