Migrer une application AngularJS en TypeScript

TypeScript dans le monde réel

Keir Lawson (NCR Edinburgh)

Traducteur

yahiko

Cet article décrit les principales étapes pour migrer de JavaScript en TypeScript une application écrite avec le framework AngularJS.

Les commentaires et les suggestions d'amélioration sont les bienvenus, alors, après votre lecture, n'hésitez pas. Commentez.

- Titre : Migrer une application AngularJS en TypeScript

- Auteur : Keir Lawson (NCR Edinburgh)

- Traducteur : yahiko

- Parution : 31 janvier 2015

- Public visé :

- Licence : Copyright ® 2015 Keir Lawson. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- 1. Avant-propos

- 2. Introduction

- 3. Modification du code

- 4. Première étape : installation de l'environnement

- 5. Deuxième étape : typage et renommage des fichiers

- 6. Troisième étape : passer aux modules, aux classes, etc.

- 7. Quatrième étape : ajouter les annotations de types

- 8. Et il y a plus

- 9. Qu'avons-nous appris ?

- 9-1. Remerciements

1 - Avant-propos

Cet article est une traduction d'un article en deux parties de Keir Lawson avec l'aimable autorisation de NCR Edinburgh.

Il s'adresse à un public de développeurs utilisant le framework AngularJS et intéressé par le langage TypeScript dont on pourra trouver une introduction ici.

2 - Introduction

Le fait que l'équipe d'AngularJS ait décidé d'adopter un surensemble du langage TypeScript, AtScript, est un signe que TypeScript est clairement le langage qui a le vent en poupe. En ajoutant des annotations de types et en reprenant les spécifications d'ECMAScript 6 (ES6), TypeScript a pour objectif de rendre les grands projets en JavaScript plus faciles à maintenir et à comprendre. Sachant cela, notre équipe a décidé d'enquêter sur l'utilisation de ce langage dans notre projet de simulateur Connections, une application AngularJS relativement grande et complexe actuellement écrite en ECMAScript 5.

Bien qu'il existe divers guides ici et là pour créer des projets AngularJS avec TypeScript à partir de zéro, peu a encore été écrit sur le portage d'applications existantes, donc ce qui suit est un résumé de notre approche et de nos conclusions.

3 - Modification du code

Comme tout effort sérieux de réécriture, là où c'est possible, une approche incrémentale est préférable. Heureusement, nous avons une suite complète de tests unitaires et fonctionnels de telle sorte que nous pouvons nous assurer que nous ne sommes pas en train de casser des choses au fur et à mesure que nous faisons évoluer progressivement le code de JavaScript à TypeScript à proprement parler. Avec cette approche, nous pouvons diviser la conversion en plusieurs étapes :

4 - Première étape : installation de l'environnement

Pour commencer, nous avons besoin un environnement où nous pouvons compiler, valider syntaxiquement (lint) et tester TypeScript tout autant que JavaScript. Même si Gulp est peut-être à la mode, pour le moment nous utilisons le système de build Grunt dans la mesure où nous avons investi dans celui-ci, en créant par exemple certains de nos propres plugins pour accompagner le développement de Connections.

Nous avons opté de façon évidente pour grunt-ts afin de gérer la compilation TypeScript. Plutôt que d'utiliser une version de TypeScript installée globalement (tsc), nous avons utilisé npm pour installer tsc localement au projet, de cette façon la version de TypeScript est contrôlée dans l'environnement du projet et les builds sont reproductibles.

Pour la validation syntaxique, tslint s'avère complet et un remplaçant simple pour jshint, ajouté à notre système de build par l'intermédiaire de grunt-tslint. Les tests restent gérés par Jasmine/Karma avec les tâches de tests impliquant désormais une étape préalable de compilation pour les tests et les testeurs.

5 - Deuxième étape : typage et renommage des fichiers

Tout d'abord, nous avons besoin de récupérer les fichiers de définition TypeScript (.d.ts) qui nous donnerons les interfaces typées vers les divers composants dont nous avons besoin, dans notre cas Angular et angular-translate pour l'application principale et Jasmine pour les tests. Ces fichiers de définitions peuvent être téléchargés manuellement ; cependant, nous avons utilisé le gestionnaire de définition TypeScript TSD qui télécharge automatiquement les définitions spécifiées dans un fichier de configuration. Cela nous permet d'éviter d'introduire les fichiers de définition dans le contrôle de version ; et ce de façon similaire à la manière d'utiliser npm et bower pour gérer les packages. Malheureusement pour ceux qui viennent de npm, l'outil TSD peut être un peu contre-intuitif, et il est regrettable de devoir introduire un autre gestionnaire de paquets, mais à ce stade il semble ne pas y avoir de meilleurs choix.

Avec les fichiers de définition en place, il ne restait plus qu'à renommer tous nos fichiers portant l'extension .js en fichiers avec l'extension .ts, ce qui est facilement réalisé avec un peu de vaudou en ligne de commande, et à insérer les références pour les fichiers de définition nécessaires. La syntaxe de ces références est un peu bizarre, mais semble être un héritage de l'intégration de TypeScript avec Visual Studio, par exemple :

///<reference path="../../../typings/angularjs/angular.d.ts"/>

Avec tout cela en place, nous avons été en mesure de compiler et d'exécuter notre suite de tests pour s'assurer que tout continuait de fonctionner comme prévu. Tout cela représente le minimum nécessaire à la conversion d'une l'application entière en TypeScript, cependant à ce stade nous ne nous sommes pas réellement appuyés sur les fonctionnalités de TypeScript au sein de notre propre code. Pour obtenir une valeur ajoutée avec cette conversion en TypeScript, nous devons faire davantage.

6 - Troisième étape : passer aux modules, aux classes, etc.

Pour rendre notre code plus idiomatique à TypeScript, il nous faut tout d'abord tirer parti des diverses fonctionnalités ES6 que fournit TypeScript, principalement les classes et les modules.

JavaScript, bien sûr, supporte déjà l'orienté objet via l'héritage par prototype, toutefois, la syntaxe des classes introduite dans ES6 et prise en charge par TypeScript facilite son utilisation. TypeScript ajoute également le support des modules qui, bien que ne s'alignant pas complètement avec la syntaxe des modules ES6, en est très proche.

Ce que cela signifie pour notre application c'est que nous devons convertir nos contrôleurs et nos services rédigés avec la syntaxe traditionnelle d'Angular et employer ces nouvelles fonctionnalités du langage. Par exemple, un simple service tel que :

'use strict';

angular.module('ccSimulatorApp')
.factory('safeApply', function ($rootScope, $timeout) {

var sa = {};

sa.apply = function() {
...
};

return sa;
});

devient

module Application.Services {
export class SafeApply {

static $inject = ['$rootScope', '$timeout'];
constructor (private $rootScope, private $timeout) {
}

apply() {
...
}

}
}

angular.module('ccSimulatorApp')
.service('safeApply', Application.Services.SafeApply);

Même si le changement de syntaxe n'est pas plus concis, il nous permet d'utiliser la vérification de types lorsque nous utilisons ce service ailleurs. Les paramètres « privés » du constructeur sont un raccourci syntaxique pour nous permettre de définir les services injectés comme des variables privées d'instance.

Il y a un écueil majeur durant cette étape ; en s'écartant des factory methods en faveur des classes appropriées, nous avons besoin d'utiliser le mot clé this pour accéder au contexte. Comme cela a été bien documenté, le this de JavaScript est une bête délicate à dompter, surtout lorsque les méthodes sont passées comme des fonctions. Cependant, la primitive bind() de ES5 nous permet d'atténuer ces problèmes. Plus d'informations sur les problèmes peuvent être trouvées ici.

7 - Quatrième étape : ajouter les annotations de types

Enfin, nous sommes maintenant en mesure de vraiment tirer parti de l'avantage de TypeScript : la vérification de types. Nous faisons cela en décorant nos signatures de fonctions avec des annotations de types et notamment celles recouvrant les fonctionnalités de base d'Angular qui sont fournies par les fichiers de définition préalablement installés. Par exemple :

constructor (private $rootScope: ng.IRootScopeService, private $timeout: ng.ITimeoutService) {
...
}

Pour des objets {…} personnalisés utilisés, nous avons besoin de créer des interfaces d'objets afin de garantir que ces objets respectent le schéma attendu. Par exemple :

interface MainConfig {
editConfig: any;
onMainContentLoaded: any;
isConfigLoaded: boolean;
}

L'ajout d'annotations de types signifie également que nous pouvons enlever n'importe quelle vérification de types programmée puisque celle-ci est gérée pour nous.

8 - Et il y a plus

TypeScript ce n'est pas juste des annotations de types. Cela apporte également d'autres fonctionnalités d'ES6 dont nous pouvons tirer profit pour notre code. Les fonctions anonymes fléchées rendent le code plus concis avec une valeur thisplus facile à prévoir. La récente version 1.4 de TypeScript ajoute également le support des variables à portée restreinte au bloc (let) et l'interpolation de chaîne (connu également comme string template). Tout cela peut nous être très utile dans notre base de code.

9 - Qu'avons-nous appris ?

Convertir en TypeScript n'est pas un processus totalement balisé. Cependant, une grande partie de l'effort que cela comporte sera de toute façon nécessaire lors de la migration à ES6/Angular 2.0. Les outils pour TypeScript sont plutôt rodés, bien que l'outil de récupération des définitions pourrait utiliser une interface utilisateur plus intuitive. La question cruciale est cependant ; est-ce utile ? Les gains de productivité d'un système avec un typage plus fort sont évidemment difficiles à jauger, mais pour ce qui concerne les grandes équipes et les grands projets, ces gains devraient être importants. En fin de compte, l'investissement dans le portage de TypeScript est une décision qui doit être faite au cas par cas, ce qui n'est pas tout à fait la conclusion définitive que nous aimerions, mais nous espérons que les grandes lignes du processus de migration présenté ci-dessus peuvent donner une idée quant à savoir si le jeu en vaut la chandelle pour votre projet.

9-1 - Remerciements

Je remercie NCR Edinburgh pour leur aimable autorisation de traduire cet article en français, ainsi que Malick SECK pour la relecture orthographique de cet article.

Les commentaires et les suggestions d'amélioration sont les bienvenus, alors, après votre lecture, n'hésitez pas. Commentez.