TypeScript, le langage web qui type statiquement JavaScript

La sortie de TypeScript l’année dernière est passée relativement inaperçue dans notre communauté Java. Pourtant, ce langage typé statiquement et compilant en JavaScript n’est pas dénué d’intérêt. La sortie de la version 0.9 est l’occasion de revenir sur les forces et les faiblesses de ce langage made in Microsoft.

Les origines de TypeScript

JavaScript est un langage souvent moqué pour sa syntaxe verbeuse et son système de types pas toujours bien compris. Pourtant, JavaScript s’est imposé depuis quelques années comme le langage indispensable au développement d’application web. Certains vont même jusqu’à dire que le JavaScript est devenu "l’assembleur du web".

Partant de ce constat, les équipes à l’origine de TypeScript se sont posé la question de savoir comment simplifier les développements d’application web tout en capitalisant sur l’existant. "Typage statique" et "programmation orientée objet avec des classes" furent les réponses apportées. Mais là où, confronté au même problème, Google décide de réécrire complètement un nouveau langage — dart — Microsoft décide de n’écrire qu’un super-ensemble de JavaScript, en y ajoutant des annotations de type, ainsi qu’un ensemble de fonctionnalités d’EcmaScript 6 tel que les classes ou les modules.

L’avantage de cette approche est évident ; tout code JavaScript est du code TypeScript, et l’apprentissage de ce nouveau langage se fait donc très rapidement pour les développeurs JavaScript qui aurait pratiqué un autre langage orienté objet à classe.

Microsoft décide par ailleurs de mettre le langage sous licence Apache et de laisser une place importante à la communauté dans les orientations que ce langage va prendre.

Quelques exemples de TypeScript

Les classes

Microsoft choisit pour son langage de respecter au maximum les spécifications de Javascript, que ce soit pour ses noms de types (string, boolean, number, etc.) ou pour sa syntaxe de classe, qui s’inspire du projet Harmony.

On aura donc par exemple :

class User {
    firstname:string; //les anotations de types s'écrivent après ':'
    lastname:string;
    age:number; //il n'y a qu'un type numérique, 'number'

    //Les méthodes s'écrivent sans mot-clef function, ou de symbole '='    
    name():string {
        return firstname + ' ' + lastname;
    }
}

Les interfaces

En plus des classes, Microsoft introduit un intéressant concept d’interface. Une classe implémentant une interface doit impérativement déclarer les méthodes définies dans cette interface, comme en Java. Cependant, une méthode attendant un type "interface" peut prendre en paramètre un objet quelconque, même si celui-ci n’implémente pas explicitement l’interface. Il suffit que la signature des attributs de l’objet correspondent à ceux de l’interface. Par exemple :

interface Point {
    x:number;
    y:number;
}

function distanceFromOrigin(p:Point) {
    return Math.sqrt(p.x*p.x + p.y*p.y);
}

distanceFromOrigin({x:3,y:4}); // compile et renvoit correctement 5

Les génériques

Depuis la version 0.9, TypeScript supporte les génériques. Un cas d’utilisation simple peut être, par exemple, sur les promesses :

//On récupère un utilisateur depuis une url
var promiseForUser: Promise<User> = $.get('/user');
var promiseForUserName: Promise<string> = promiseForUser.then((user:User) => {
    return user.name;
});

//Une fois la réponse recue, on affiche le nom de l'utilisateur
promiseForUserName.done((name:string) => {
    console.log(name);
});

Scoping des fonctions

TypeScript permet d’écrire les fonctions avec une syntaxe raccourcie. La syntaxe est celle choisie par EcmaScript 6, avec le symbole => :

var add = function(a:number,b:number):number {
    return a + b;
}

est équivalent à

var add = (a:number,b:number):number => a + b;

De plus, cette syntaxe rescope le this :

{
   a:2,
   addToList: function(numberList:Array<number>):Array<number> {
           //dans le map, this.a vaut bien 2
           //si on avait utiliser la syntaxe classique, avec le mot clef function
           //this.a aurait été undefined.
           return numberList.map((nb) => nb + this.a);
}

Les modules

Pour isoler tout ce code, TypeScript propose un système de module directement importé, une fois de plus, d’EcmaScript 6 :

module Hero {
    export var batman = {trueName: 'Bruce Wayne'};
}

console.log(Hero.batman.trueName); // Affiche 'Bruce Wayne'

Les déclarations de librairies

TypeScript se veut complètement compatible avec JavaScript et permet donc, via une syntaxe spécifique, de déclarer l’utilisation de librairie externe, et même de forcer les types de l’API de cette librairie externe. Par exemple, pour Jquery :

declare var $; //permet d'utiliser la variable '$'

$(document).on('click', () => {
    alert('click !');
});

En plus de cette syntaxe manuelle, il est possible d’importer des fichiers de définitions, analogues aux fichier header en C

//permet de définir un type pour toutes les API jQuery
/// <reference path="jquery.d.ts" /> 

$(document).on('click', () => {
    alert('click !');
});

TypeScript, en vrai

Sur le papier, les fonctionnalités supportées par TypeScript sont séduisantes. Mais, TypeScript possède aussi quelques mauvais cotés.

L’intégration de librairie externe

Pour bénéficier du typage statique dans les librairies externes, il est nécessaire d’utiliser des fichiers de définitions. Ces fichiers indiquent les signatures de toutes les fonctions/méthodes de la librairie externe. Il est assez fastidieux d’écrire ce genre de fichier, mais la communauté est là pour vous : https://github.com/borisyankov/DefinitelyTyped fournit les fichiers de définitions pour les librairies JavaScript les plus connues du marché.

Malheureusement, les fichiers de définitions ne sont pas toujours à jour et il est assez fastidieux d’écrire le sien. Il est toujours possible d’utiliser une librairie sans ses définitions, mais il est frustrant, dans un langage typé statiquement, de finalement devoir se retrouver avec le type any partout (any est le type représentant tous les autres types).

L’intégration dans un IDE

Microsoft a mis de gros moyens en œuvre pour le support de TypeScript dans Visual Studio. Malheureusement, pour nous, développeurs Java, les choses sont beaucoup moins roses. Intellij IDEA (et ses dérivés, PHPStorm, WebStorm, etc.) supporte presque correctement la version 0.8.3 de TypeScript, mais ne supporte, pour l’instant, pas du tout la version 0.9. Quant à Eclipse, il ne semble pas avoir de plugin dédié à TypeScript.

L’aspect statique

Cela semble évident, mais le fait que TypeScript soit compilé statiquement fait perdre en grande partie le coté dynamique de JavaScript. Cela pose un problème lorsque l’on souhaite utiliser un objet comme un "dictionnaire". Dans un cas comme celui-là, comme TypeScript ne permet pas l’ajout à la volée de propriété, on se retrouve à être obligé de caster l’objet en any et donc de perdre le typage statique. Dans le meilleur des cas, on se retrouve avec un code inutilement verbeux, comme ci-dessous :

class Dog {
    constructor(public name:string){}
}

var milou:Dog = new Dog('milou');
// sans le cast, le code ci-dessous ne compile pas.
(<any>milou).tatouage = '12234'; 

La fausse promesse du support d’EcmaScript 5

TypeScript, par défaut, permet d’écrire du code compatible EcmaScript 5 :

interface Livre {
    titre:string;
}

class Bibliotheque  {
    constructor(private livres:Array<Livre>) {    
    }

    // Les getter n'existe qu'en EcmaScript 5
    get titres() {
        // La méthod map de Array  n'existe qu'en EcmaScript 5
        return this.livres.map((livre) => {
            return livre.titre;
        });
    }
}

Dans le cas ci-dessus, avec le mode de compilation par défaut, ce code ne fonctionnera pas sur Internet Explorer 8, car ni map ni les getter n’existent.

Le mode de compilation EcmaScript 3 renverra une erreur de compilation sur le getter mais pas sur le map.

Cela signifie que TypeScript ne résout pas le problème de rétrocompatibilité avec les vieux navigateurs. Le langage a donc le même défaut que JavaScript en ceci qu’on ne pourra en tirer toute sa quintessence qu’en travaillant uniquement sur navigateur récent.

TypeScript sur un projet Java

Le compilateur de TypeScript est écrit en JavaScript, ce qui nécessite donc pour le compiler d’utiliser ou node ou Rhino (si on aime aller prendre un café à chaque modification de fichier). Heureusement, un compilateur en Java pur a été écrit, de façon à optimiser grandement les performances. L’utilisation "à la main" de ce genre de projet étant toujours laborieuse, je ne peux que vous inviter à vous pencher sur la solution wro4j qui intègre le compilateur écrit en Java, et qui est très simple à configurer.

Le futur de TypeScript

La sortie de la version 0.9 est une étape importante sur le chemin de la V1 de TypeScript. Les génériques sont une nouveauté qui fait passer le langage dans la cour des grands : on va enfin pouvoir y coder des monades typées !

Le bug tracker de TypeScript permet de voir quelles sont les fonctionnalités que l’on peut attendre pour la V1 de ce langage. L’objectif semble dorénavant d’implémenter un maximum de fonctionnalités d’EcmaScript 6, comme les fonctions génératrices, ou la structure "for…of".

Les performances du compilateur seront aussi revues à la hausse, de façon à rendre l’utilisation de TypeScript sur un projet réellement agréable.

Conclusion

À la question "TypeScript est-il utilisable en production ?", je pense qu’aujourd’hui la réponse est oui. Le langage n’est pas parfait, mais ce qu’il apporte est suffisant pour justifier sa mise en place, à condition que le projet contienne beaucoup de JavaScript. En effet, il y a un coût de mise en place et d’utilisation non-nul. Sur un projet où l’on se contenterait de faire un peu de JQuery pour l’apparence graphique, l’utilisation de TypeScript ne serait pas justifiée. Pour les gros projets utilisant des frameworks MVC client (Angular, Backbone, Ember, etc.), TypeScript peut se révéler un allié précieux, mais qui malheureusement ne pourra pas vous servir dans toutes les situations (par ex. les modèles objet de Backbone et Ember ne sont pas compatibles avec le modèle objet de TypeScript sans manipulations complexes). Si vous regrettez l’époque bénie où les langages typées statiquement dominaient le monde, TypeScript vous mettra aux anges, et vous ne devriez pas hésiter à le mettre en place sur vos projet.

Billets sur le même thème :

One Response

  • Article intéressant, type script semble répondre à la problématique de typage qui fait fuir tous les dév (java, c, c++, c#, ) de javascript. je l’ai essayer et je trouve que je vais déjà trois fois plus vite. je n’ai plus besoin d’aller sur internet toute les deux minutes pour regarder comment (ajouter un élément dans un tableau, comment récupérer le nombre d’élément d’un tableau … ) c’est le pied quoi.

Laisser un commentaire