ECMAScript 6 et TypeScript
La proximité temporelle entre la version 1.5 alpha de TypeScript (TS) annoncée récemment et la prochaine validation officielle de la norme ECMAScript 6 (ES6) tant attendue, en principe en juin est l'occasion de faire le point sur les fonctionnalités ES6 supportées par le langage de Microsoft.
Il existe un site recensant le niveau de compatibilité avec la norme ES6 des différents navigateurs et transpileurs JavaScript.
Bien entendu, la version 1.5 alpha de TS y est référencée, dans la catégorie compilateurs/polyfills.
On constate qu'en terme de couverture brute, TS est à 25% à comparer par exemple au 75% de Babel (anciennement 6to5). On est encore loin du compte certes mais contrairement à Babel ou à Traceur qui se veulent de purs transpileurs avec justement pour unique objectif de transpiler en ES5 du code ES6, TS se veut être plus qu'un transpileur, mais bien un langage à part entière avec des contraintes qui lui sont propres.
On constate d'ailleurs si on considère un langage qui comme TS n'est pas uniquement un transpileur, que le taux de couverture brute de Clojure, de 29%, est similaire à celui de TS.
Il faut aussi considérer ce taux de couverture de 25% avec quelques précautions dans la mesure où toutes les fonctionnalités ne sont pas d'égale importance en pratique.
Toujours est-il que ce tableau classe les différentes fonctionnalités de la norme ES6 en trois catégories : utile, significative et emblématique (landmark). Focalisons-nous sur cette dernière catégorie, une revue exhaustive étant beaucoup trop longue pour votre humble serviteur, et regardons la couverture de TS concernant ces fonctionnalités dites emblématiques (même s'il faut garder à l'esprit que cette classification reste subjective et propre à ce site).
Syntaxe :
Opérateur d'expansion (...) : 2/10
L'opérateur d'expansion (spread) permet d'extraire sous la forme d'une séquence séparée par des virgules, l'ensemble des éléments d'une collection.
TS 1.5 supporte le cas le plus évident, celui avec un tableau.
var x = Math.max(...[1, 2, 3]);
est équivalent à
var x = Math.max(1, 2, 3);
TS 1.5 ne prend pas en charge les autres cas moins naturels (et moins fréquents) prévus par la norme concernant les chaînes de caractères. Il n'est donc pas possible d'utiliser l'opérateur comme ceci :
var x = Math.max(..."123"]);
TS 1.5 ne prend pas non plus en charge le cas plus général des objets itérables génériques définis à l'aide de la fonction global.__createIterableObject().
Extensions des objets littéralement définis : 5/6
Lorsqu'on définissait explicitement un objet {...} , ses propriétés ne pouvaient avoir qu'un nom littéral, c'est-à-dire être un symbole terminal dans le jargon de la théorie des langages. Par exemple :
1 2 3
| var obj = { a: 1, b: 2 };
obj.a === 1;
obj.b === 2; |
Les propriétés a et b de l'objet obj ainsi défini sont désignées explicitement par un identificateur. Or, il était également possible de définir les propriétés d'un objet avec des noms calculés, via une approche dynamique :
1 2 3 4 5 6 7
| var x = 'a';
var y = 'b';
var obj = {};
obj[x] = 1;
obj[y] = 2;
obj.a === 1;
obj.b === 2; |
Avec la norme ES6, cette frontière disparaît et il est possible de définir explicitement un objet avec des noms de propriétés calculées :
1 2 3 4 5
| var x = 'a';
var y = 'b';
var obj = { [x]: 1, [y]: 2 };
obj.a === 1;
obj.b === 2; |
TS 1.5 prend en charge cette nouvelle fonctionnalité, à l'exception des accesseurs get et set qui eux doivent encore être nommés avec un littéral.
Boucle for..of : 2/7
Cette nouvelle construction de boucle permet d'itérer sur une collection de façon plus pratique qu'avec la construction for..in puisque la variable de contrôle n'est plus la clé de l'élément itéré mais bien sa valeur :
1 2 3 4 5 6
| var arr = ['Hello ', 'world! '];
var sentence = '';
for (var item of arr) {
sentence += item;
}
sentence === 'Hello world! '; |
TS 1.5 permet d'utiliser cette nouvelle construction pour itérer sur des tableaux et des chaînes de caractères.
Cependant, cette construction en TS 1.5 ne prend pas encore en charge l'Unicode étendu (plan astral) ni les objets itérables génériques.
Interpolation de chaînes : 2/2
L'interpolation de chaînes de caractères permet d'une part de fractionner les chaînes littérales sur plusieurs lignes et permet d'autre part d'exprimer certaines parties d'une chaîne littérale par une expression qui sera évaluée à l'exécution. Cela permet d'améliorer la lisibilité du code en limitant l'usage massif de concaténations, notamment lorsqu'il s'agit de construire du code HTML à la volée.
1 2 3 4
| var a = "ba", b = "QUX";
var str = `foo bar
${a + "z"} ${b.toLowerCase()}`
str === "foo bar\nbaz qux"; |
TS 1.5 supporte pleinement cette nouvelle fonctionnalité.
Déstructuration : 20/30
L'affectation par décomposition, ou plus littéralement la déstructuration, permet d'éviter l'usage de certaines variables intermédiaires et rend ainsi le code plus lisible lorsqu'il s'agit de manipuler des n-uplets (ou tuples) soit sous la forme de tableaux ou sous la forme de chaînes de caractères.
1 2 3 4 5
| var a, b;
[a, b] = [1, 2];
a === 1 && b === 2;
[a, b] = [b, a];
a === 2 && b === 1; |
TS 1.5 supporte cette fonctionnalité pour l'essentiel, à part les cas liés aux objets itérables génériques, au passage en paramètres du constructeur de Function(), et quelques bugs probablement en cours de correction.
Fonctions :
Fonctions anonymes fléchées : 8/11
La notation fléchée permet de simplifier l'écriture des fonctions anonymes dans la mesure où sa notation est plus concise et où elle permet la conservation du contexte this de la fonction de portée lexicale immédiatement supérieure, ce qui évite l'utilisation intempestive de variables intermédiaires _this ou that ou bien l'abus de bind(). Cette notation fléchée permet aussi la conservation du contexte arguments de la fonction de portée lexicale immédiatement supérieure.
1 2 3 4 5 6 7 8 9 10
| function AlertMsg1(msg) {
this.msg = msg;
this.timer = setTimeout(function () { alert(this.msg); }, 1000);
}
function AlertMsg2(msg) {
this.msg = msg;
this.timer = setTimeout(() => { alert(this.msg); }, 2000);
}
var a1 = new AlertMsg1('Hello world!'); // undefined
var a2 = new AlertMsg2('Goodbye folks!'); // 'Goodbye folks!' |
TS a supporté cette fonctionnalité très tôt et on la retrouve évidemment dans la version 1.5. Seuls quelques manquements à la norme subsistent comme l'absence de la conservation du contexte arguments ou le fait que cette fonction anonyme fléchée possède anormalement une propriété prototype.
Classes : 14/23
Les classes sont l'un des piliers de la programmation orientée objet au sens classique du terme.
1 2 3 4 5 6 7 8 9
| class C {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
c = new C(1, 2);
c.a === 1;
c.b === 2; |
TS a intégré les classes depuis ses débuts, cela a même été une de ses motivations premières, dans la mesure où la notion de classe s'inscrit dans la notion plus générale des types. La version 1.5 de TS a encore quelques lacunes par rapport à la norme ES6 comme la possibilité d'imbriquer des classes ou la notion de classe anonyme.
Générateurs : 0/21
Les générateurs sont une possibilité offerte par la norme ES6 de faciliter le multitâche coopératif à l'aide de fonctions dites génératrices et du mot-clé yield.
1 2 3 4 5 6 7 8 9 10 11
| function* coop(){
var index = 0;
while (index <= 2)
yield index++;
};
var iterator = coop();
iterator.next().value === 0;
iterator.next().value === 1;
iterator.next().value === 2;
iterator.next().value === undefined; |
TS 1.5 n'implémente pas encore cette fonctionnalité. Elle est prévue dans la version suivante, la 1.6.
Prédéfinitions :
Tableaux typés : 0/40
Les tableaux typés sont une fonctionnalité de la norme ES6 permettant des optimisations lorsqu'il s'agit de manipuler de grandes quantités de données, souvent de même nature ou de même format, prédéterminé à l'avance.
TS 1.5 ne supporte cette fonctionnalité qu'en ciblant du code ES6. Il ne peut pas transpiler cette construction en code ES5 pour le moment.
Proxy : 0/20
Les proxy généralisent le concept d'accesseur en permettant de personnaliser le comportement d'un objet lors des accès à ses propriétés ou lors de son instanciation via l'opérateur new.
TS 1.5 ne supporte cette fonctionnalité qu'en ciblant du code ES6. Il ne peut pas transpiler cette construction en code ES5 pour le moment.
Promesses : 0/3
Les promesses sont un design pattern assez commode pour simplifier la programmation asynchrone, surtout lorsqu'il s'agit de traiter non pas un événement mais une collection d'événements.
TS 1.5 ne supporte cette fonctionnalité qu'en ciblant du code ES6. Il ne peut pas transpiler cette construction en code ES5 pour le moment. Les promesses seront à l'ordre du jour dans la version 1.6 en parallèle des constructions async/await.
Conclusion
Ce petit tour d'horizon de la couverture ES6 de TS n'est évidemment pas exhaustif puisque les fonctionnalités classées en utiles et significatives n'ont pas été abordées.
Il faut de plus garder à l'esprit que TS n'est pas un transpileur pur mais un langage à part entière qui en plus des fonctionnalités ES6 qu'il intégrera à terme, propose d'autres concepts et notamment le typage (annotations de type, unions de types, interfaces, ...).
Cela n'a donc pas vraiment de sens d'opposer ES6 à TS comme il est parfois possible de lire en se promenant sur la toile. ES6 ne va pas rendre TS obsolète, tout comme TS n'a pas vocation à remplacer ES6.
Enfin, concernant la couverture de la norme ES6 par TS, même si toutes les fonctionnalités peuvent ne pas être transpilées en fin de compte en ES5, la mise à la norme des navigateurs rendra ce besoin moins crucial au fur et à mesure que le temps s'écoulera. Il ne faut donc pas faire du tableau de couverture fonctionnelle le critère absolu de l'alignement de TypeScript avec la norme ES6.
Partager