Où en est TypeScript vis à vis de ECMAScript 6 ?
Présentation de ce que TypeScript implémente pour le moment

Le , par yahiko, Rédacteur/Modérateur
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.
Code : Sélectionner tout
var x = Math.max(...[1, 2, 3]);
est équivalent à
Code : Sélectionner tout
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 :
Code : Sélectionner tout
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 :
Code : Sélectionner tout
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 :
Code : Sélectionner tout
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 :
Code : Sélectionner tout
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 :
Code : Sélectionner tout
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.
Code : Sélectionner tout
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.
Code : Sélectionner tout
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.
Code : Sélectionner tout
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.
Code : Sélectionner tout
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.
Code : Sélectionner tout
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.


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de Paleo Paleo - Membre confirmé https://www.developpez.com
le 16/04/2015 à 9:46
Jusqu'ici l'équipe de TypeScript s'est strictement tenue à un principe : le code ES3/5 généré ne doit dépendre d'aucune bibliothèque JS à l'exécution.

Pour une VM ES3/5, cela élimine toutes les nouvelles API de ES6 qui nécessitent des polyfills, dont l'objet Symbol (et donc les itérateurs), les Map, Set et autres tableaux typés, les proxies, promesses, l'API Reflect pour ne citer que les plus attendues. Ce sont des fonctionnalités utilisables en TypeScript mais le format du code généré doit alors être ES6.

Certaines API peuvent être utilisées en TypeScript sur une VM ES3/5 grâce à des polyfills autonomes : les promesses, les Map, Set, Reflect, et un bon nombre d'extensions dans les API existantes. Par exemple avec es6-shim. Mais, par design, les fonctionnalités qui nécessitent une collaboration entre le transpileur et le polyfill ne sont pas accessibles en TS : en particulier, les itérateurs.

Concernant les générateurs prévus dans TypeScript 1.6, je me demande bien comment ils vont faire. Babel utilise sa lib JS.

Citation Envoyé par yahiko Voir le message
ES6 ne va pas rendre TS obsolète
Contrairement à Babel, ex 6to5. Mais ses développeurs ont pris les devants en renommant leur projet. Babel est désormais le transpileur qui implémente les futures normes au niveau 2 (draft).
Avatar de SylvainPV SylvainPV - Rédacteur/Modérateur https://www.developpez.com
le 16/04/2015 à 10:29
Je me demande si ce ne serait pas plus simple pour eux de transpiler TypeScript en ES6 puis d'utiliser Babel à la volée pour transpiler en ES5. On ne capitalise pas beaucoup sur le travail fait par ces outils open-source.
Avatar de Paleo Paleo - Membre confirmé https://www.developpez.com
le 16/04/2015 à 10:48
Citation Envoyé par SylvainPV Voir le message
Je me demande si ce ne serait pas plus simple pour eux de transpiler TypeScript en ES6 puis d'utiliser Babel à la volée pour transpiler en ES5. On ne capitalise pas beaucoup sur le travail fait par ces outils open-source.
Tout d'abord TypeScript compile des classes ES6 depuis 2012, bien avant Babel/6to5. Dans le cadre d'une exécution sur une VM ES3/5, il ne faut pas se voiler la face, Babel et TypeScript sont deux outils partiellement concurrents.

Et puis il y a des cas où TypeScript est par design plus performant que Babel. Prenons ce code en ES6:

Code : Sélectionner tout
1
2
3
4
let arr = ['a', 'b', 'c'];
for (let item of arr) {
  console.log(item);
}
TypeScript 1.5-aplha le transpile ainsi :

Code : Sélectionner tout
1
2
3
4
5
var arr = ['a', 'b', 'c'];
for (var _i = 0; _i < arr.length; _i++) {
    var item_1 = arr[_i];
    console.log(item_1);
}
Mais voici ce que donne la version Babel (sur le REPL):

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
'use strict';

var arr = ['a', 'b', 'c'];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;

try {
  for (var _iterator = arr[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
    var item = _step.value;

    console.log(item);
  }
} catch (err) {
  _didIteratorError = true;
  _iteratorError = err;
} finally {
  try {
    if (!_iteratorNormalCompletion && _iterator['return']) {
      _iterator['return']();
    }
  } finally {
    if (_didIteratorError) {
      throw _iteratorError;
    }
  }
}
Pourquoi cette horreur sur Babel ? À cause des itérateurs. En effet, Babel ne dispose pas d'informations sur le type de la variable "arr". Par conséquent il fait appel à son polyfill de "Symbol" systématiquement, alors que pour itérer sur un simple tableau c'est inutile.

Peut-être qu'un jour TypeScript fournira un polyfill pour Symbol mais il saura de toute manière toujours générer du code élégant pour les tableaux dont il connait le typage.
Avatar de SylvainPV SylvainPV - Rédacteur/Modérateur https://www.developpez.com
le 16/04/2015 à 14:38
Ok je comprends mieux. Mais Babel ne peut-il pas reconnaître les Arrays par inférence, puisqu'il a tout le code source à disposition ? J'ai l'impression que TypeScript s'est concentré dès le départ sur les performances tandis que Babel voulait le plus de fonctionnalités ES6 possibles.
Avatar de yahiko yahiko - Rédacteur/Modérateur https://www.developpez.com
le 16/04/2015 à 14:56
Pour Babel, je ne sais pas exactement, même si oui je pense qu'il a priorisé sur les fonctionnalités ES6 avant tout.
Pour TypeScript, je dirais qu'avant les performances, il s'attache à restituer du code JavaScript aussi proche que possible de l'original TypeScript.
Avatar de Paleo Paleo - Membre confirmé https://www.developpez.com
le 16/04/2015 à 15:06
La performance par l'élégance est la bonne manière de faire.

Babel est un projet plus léger que TypeScript. Il n'implémente pas d'inférence de type, il ne génère aucune erreur, par exemple, si l'on remplace l'initialisation de arr par : "let arr = {};". Pourtant les objets ne sont pas itérables. Mais de toute manière il suffirait que la variable "arr" soit un paramètre passé à une fonction pour empêcher l'inférence en syntaxe ES6 pure.

L'inférence à la manière de TypeScript parait naturelle et intuitive, mais je ne pense pas que son implémentation soit une petite affaire. En fait il est assez possible que 80% du code du compilateur TS soit destiné au parseur et à la représentation interne du code analysé, et que les sorties prennent les 20% restant : la génération des codes ECMAScript plus TS Server (l'analyse syntaxique prête à emploi pour les éditeurs). Donc il serait dommage que TypeScript se prive de terminer le travail. Sans parler des soucis de performance du compilateur lui-même : s'il fallait ajouter un deuxième niveau de transpilation, à l'usage, ça ralentirait.
Avatar de Greg-dev Greg-dev - Membre régulier https://www.developpez.com
le 17/04/2015 à 11:56
Citation Envoyé par yahiko Voir le message
ES6 ne va pas rendre TS obsolète
Par contre quand Javascript implémentera ES6, je ne verrais plus d'intérêt à Typescript ou même Dart
Avatar de yahiko yahiko - Rédacteur/Modérateur https://www.developpez.com
le 17/04/2015 à 12:54
J'ai du louper quelque chose dans mon explication alors.
TypeScript apporte le typage statique, d'où son nom, et permet ainsi d'écrire des applications Web de grandes envergures plus facilement et de façon plus fiable qu'avec du JavaScript pur, y compris ES6.
Contacter le responsable de la rubrique TypeScript