Accueil Nos publications Blog Retour d’expérience sur TypeScript

Retour d’expérience sur TypeScript

TypeScript & ECMAScript

Après avoir lu il y a environ 4 ans le livre Javascript: The Good Parts de Douglas Crockford dont je vous recommande vivement la lecture, je suis devenu un amateur avisé du langage JavaScript. Pour un développeur C#, cela me semble pas si commun. Par contre, il n’est pas aisé de faire de la POO en JavaScript. En fait, pour Crockford, il y a de meilleurs patterns possibles en JavaScript, mais c’est un autre débat. Je ne pouvais donc qu’être intéressé par TypeScript qui apporte en gros au JavaScript à la fois un peu du C#/Java (typage, héritage, encapsulation, generic…) et certaines avancées proposées avec les dernières versions d’ECMAScript : ES5, ES6 et ES7.

J’ai eu l’opportunité de travailler à la transition d’un site Web de JavaScript à TypeScript, site sur lequel j’intervenais depuis un an et demi en la qualité de développeur full stack. J’ai alors constaté le relatif manque d’informations concrètes pour réaliser sereinement cette transition. Cet article vise à combler en partie ce manque, en partant de mon retour d’expérience sur ce projet d’une durée d’un an et des techno utilisées : ASP.NET MVC avec les frameworks jQuery et Knockout-JS.

Problématiques initiales

La transition des scripts vers TypeScript a d’abord été motivée par les problématiques suivantes, rencontrées sur le code Legacy issu de la version précédente du site.

Redondance complexe

De nombreux scripts présentaient des portions de code très similaires et non mutualisées, c’est-à-dire avec des fonctionnalités en partie communes, en partie spécifiques. Ainsi, il m’est arrivé de devoir répliquer une modification sur 18 scripts, d’où l’augmentation du risque d’erreur, la perte de temps en développement et en tests manuels.

Variables globales

Pour transmettre des données entre scripts ou des vues CSHTML aux scripts, le code existant utilisait beaucoup de variables globales JavaScript, pratiquement une variable par donnée, plutôt que de les rassembler dans des objets littéraux. Cela ouvrait la voie aux problématiques « classiques » avec les variables globales telles que le manque de cohérence, le télescopage, la pollution de l’objet global window, la possible perte de performance quant à l’accès à la mémoire…

Encodage manuel en JavaScript

Fonctionnant de manière classique avec ASP.NET MVC, les données sont transmises aux scripts en passant par les vues CSHTML. Le système se basait sur un encodage manuel en JavaScript dont voici un exemple illustrant les 3 méthodes utilisées, l’image étant une capture d’écran de Visual Studio :

Code d'une vue CSHTML avec 3 manières d'encoder en JavaScript[/caption]

Méthode manuelle et unitaire

Cela consiste à écrire chaque donnée une à une, en l’associant soit à une variable, soit à une propriété d’un objet littéral JS, cf. flèche 1 ci-dessus. C’est répétitif, source d’erreur et pas facile à lire. De plus, avec la syntaxe Razor, Visual Studio détecte des fausses erreurs de syntaxe dans le JS, cf. flèche rouge. On aimerait enfin se passer des librairies de conversion telles que Globalize.JS.

Méthode améliorée

Il s’agit d’utiliser la méthode JSON.Encode (2b) pour réaliser l’encodage en une seule fois d’un objet C# (2a). Ainsi, on réduit le nombre de variables globales et l’on ne se soucie pas de la manière d’encoder une donnée selon son type. Par contre, on a encore la fausse erreur de syntaxe. Plus gênant, les dates ne sont pas des vraies Date JS comme on peut le constater dans les traces dans une console JS :

encode-js-trace

Pour pallier ce problème, on peut encapsuler le tout dans une nouvelle méthode d’extension du HtmlHelper, cf. Html.DeclareJsObject (2c), et y modifier le JSON en sortie pour transformer les expressions “/Date(xxx)/” en Date JS, une expression régulière faisant bien l’affaire :


public static class JsExtensions
{
    public static IHtmlString DeclareJsObject(this HtmlHelper htmlHelper,
                       Expression<Func<object>> getObjectExpression, string objectName = null)
    {
        var value  = ((LambdaExpression)getObjectExpression).Compile().DynamicInvoke();
        objectName = objectName ?? ExpressionHelper.GetExpressionText(getObjectExpression);
        var js     = EncodeJsObject(value);
        var result = string.Format("var {0} = {1};", objectName, js);
        return htmlHelper.Raw(result);
    }

    public static string EncodeJsObject(object value)
    {
        var json = Json.Encode(value);
        var dateRegex = new Regex(@"(""\\/)(Date\(\d+\))(\\/"")", RegexOptions.None);
        return dateRegex.Replace(json, @"new $2");
    }
}

A propos de TypeScript

Philosophie

La philosophie au cœur du TypeScript tient aux 3 points exposés sur sa page d’accueil :

Commence et finit avec JavaScript

TypeScript part du JavaScript, sa syntaxe et sa sémantique, connu de millions de développeurs JavaScript. Le code JavaScript existant s’interface naturellement avec celui de TypeScript, et réciproquement moyennant quelques astuces. Cela permet l’emploi des librairies JavaScript populaires telles qu’Angular, JQuery, Knockout, React, Rx. C’est ce qui le différencie des autres transpileurs tels que CoffeeScript.

Le code TypeScript est transpilé en code JavaScript propre, simple et compatible avec tout moteur JavaScript supportant l’ECMAScript 3, tels que Node.js et tous les navigateurs de ces dernières années.

Outils robustes pour larges applications

Le typage permet aux outils de développement de proposer une complétion de code (aka intellisense) fiable, de la vérification statique des types et du refactoring de code. Les développeurs gagnent en productivité, les produits sont plus fiables.

La combinaison du typage optionnel et de l’inférence de type permet de réduire les définitions explicites du type tout en bénéficiant de ses avantages en termes de vérification statique du code.

Il est possible de typer les librairies JavaScript existantes afin de gagner en visibilité sur leur comportement une fois utilisée en TypeScript.

Etat de l’art JavaScript

TypeScript est « ECMAScript friendly ». Il tend à offrir un support aux fonctionnalités et évolutions les plus récentes de JavaScript, même à l’état de proposition, définies dans le standard ES7. On trouve ainsi de manière non exhaustive :

  • les fonctions async et les décorateurs, chers à Angular2,
  • les fonctions flèches aka expression lambda, permettant une écriture plus compacte et lisible et de fiabiliser le ‘this’,
  • la substitution de chaîne, dont la syntaxe est proche du C#6,
  • les mots clés let/const permettant d’avoir des variables de portée au niveau bloc, plus intuitive que l’habituelle portée de niveau fonction,
  • l’opérateur spread ‘…’ autorisant une syntaxe explicite pour les arguments restant d’une fonction et dont le type est vraiment Array, de même que le clonage et la concaténation de tableaux (depuis la version 1.5) et des objets littéraux (en version 2.1 d’après la roadmap).

Attention cependant ! Cela se passe à deux niveaux : de base, ils sont reconnus dans le code TypeScript. Mais à l’heure actuelle, seules certaines de ces fonctionnalités peuvent être transpilées en JavaScript compatible avec une version inférieure d’ECMAScript comme le fait Babel, lorsque l’on spécifie une target de ce type au transpiler.

Spécificités

Voici quelques-unes des spécificités de TypeScript par rapport au JavaScript :

  • Généralités : améliore et sécurise la programmation de code JS.
  • Typage statique, simple ou combinable par union
  • C#/Java friendly : enum, type générique, paramètres optionnels
  • POO : classes concrètes/abstraites, interfaces, héritage simple.
  • Encapsulation et gestion des noms grâce aux modules et namespaces.

Pérennité

Etant donné le rythme élevé quasi frénétique de nouveaux outils et librairie dans le monde du développement Front, la pérennité du TypeScript est une vraie question. Il vaut mieux parier sur le bon cheval pour ne pas se retrouver avec un langage peu connu, peu aimé, plus maintenu…

De ce côté-là, les signes sont plutôt favorables :

  • Sa pérennité est directement lié à celle du JavaScript dont on peut être tranquille : non seulement il devient le langage incontournable pour les navigateurs Web, avec la mort bien amorcée de Flash, sans parler de Silverlight, mais il a de nombreux supporters pour les développements serveurs grâce à Node.js.
  • Typescript, c’est le sérieux d’Anders Hejlsberg, l’un des pères du C#.
  • Angular2, une des librairies les plus prometteuses à mes yeux en particulier pour des SPA, est écrite en partie en Typescript et ses auteurs encourage son usage en alternative à Babel pour avoir le typage fort et la POO – cf. la page ES6/TYPESCRIPT/ATSCRIPT/BABEL…WAT?.
  • La philosophie « starts and ends with JavaScript » permet de coller aux nouveaux standards tout en assurant la compatibilité avec les navigateurs Web.

Conclusion de cette partie

Nous avons vu quelques problématiques d’un site Internet utilisant le JavaScript et quelques éléments clés et prometteurs du langage TypeScript semblant répondre à ces problématiques tout en rendant les développements plus fiables et le site plus robuste. Nous verrons dans une prochaine partie les solutions mises en œuvre et les difficultés rencontrées au passage.

© SOAT
Toute reproduction interdite sans autorisation de la société SOAT