Édition d’avril – mai 2015 Numéro 57 Magazine en ligne gratuit Diffusion de copies conformes à l’original autorisée Réalisation : Alexandre Pottiez Rédaction : la rédaction de Developpez Contact : [email protected] Sommaire Article Programmation C++ Page 2 JavaScript Page 6 Java Page 20 Eclipse Page 29 Android Page 46 Le débogage d’une application : mé- Programmation Page 53 2D/3D/Jeux Page 68 thodes et exercices Qt Page 71 Access Page 82 Vous êtes face à un bogue et vous ne savez pas où il se trouve. Excel Page 99 Onvousditd’utiliserun«débogueur».Vousenavezun,mais vousnel’avezjamaisutiliséetvousnesavezpasquoifaire.Cet article est pour vous! par Alexandre Laurent Éditorial Page 53 Le magazine revient avec toujours Article 2D/3D/Jeux plus d’articles pour satisfaire votre envie de connaissance. Vulkan, la nouvelle bibliothèque de hautes performances pour le GPU Découvrez la nouvelle bibliothèque pour le GPU de Khronos. Celle-ci se place comme successeur d’OpenGL. par Alexandre Laurent Page 68 La rédaction C++ Les derniers tutoriels et articles Métaprogrammation et métafonctions en C++11 1 Introduction LesfonctionsetclassestemplateenC++03ainsi a peu, d’où l’utilisation peut-être excessive du prin- que la possibilité de les spécialiser, ont permis la cipe des templates. créationd’outilspuissants.L’undesplusimportants Un code qui utilise la métaprogrammation n’est restesansdoutelabibliothèqueBoost.MPL(lien1), pas facile à lire ni à écrire; en contrepartie, son em- un framework de métaprogrammation de template ploi permet quelque chose de fantastique : effectuer de haut niveau. des calculs et utiliser leur résultat au moment de la Tout le monde n’a pas besoin de connaître ou compilation! d’utiliser la métaprogrammation pour être un bon Heureusement, la norme C++11 a apporté son programmeur, mais l’outil peut sembler trop théo- lot d’outils pour la rendre plus abordable et facile rique. Cependant, il y a des domaines où la méta- d’accès. De plus en plus de programmeurs peuvent programmation est utile : par exemple, les traits ou utilisercestechniquessanspasserdesnuitsblanches les analyses dimensionnelles (lien 2) effectuées à la à comprendre la mécanique des templates ou à ana- compilation. lyser de mystérieux messages d’erreur du compila- Enrevanche,l’emploidecestechniquesn’estpas teur. Dave Abrahams a participé à un débat sur toujours conseillé pour la simple raison qu’elles sont le thème de la « métaprogrammation en C++11 » trèsdifficilesàutiliserpourleprogrammeurlambda. (lien 3) lors de la conférence C++ Now! de 2012, à La faute en incombe à la conception du C++03 qui Aspen, dans le Colorado. n’était pas très orientée vers le support de la mé- Voici maintenant une petite introduction sur taprogrammation qui, de plus, a été découverte il y deux concepts de base de la métaprogrammation. 2 Qu’est-ce qu’une métafonction? Toutd’abord,quesignifielepréfixe«méta»ici? rement aux fonctions « classiques » qui se lancent Ma définition est que c’est un bout de code pou- normalementdurantl’exécutionduprogramme,une vantêtreexécutéaucoursdelacompilationdupro- métafonctionpeutrenvoyerdeuxchoses:unevaleur grammeetsonrésultatutiliséàcemoment.Contrai- ou un type. 3 Calcul des valeurs LecalculdesvaleursàlacompilationenC++03 9 { est basé uniquement sur le comportement des tem- 10 static const int value = i * j; plates qui, en dehors des types, peuvent aussi être 11 } paramétrés avec des valeurs entières. Par exemple : La structure IntArray nous montre que les pa- 1 template<int i> ramètres de template non typés ont réellement été 2 struct IntArray ajoutés pour le C++03. 3 { 4 int value[i]; La structure Multiply nous montre un exemple 5 }; simple de métafonction : on donne deux valeurs de 6 type int et nous pouvons en trouver une troisième 7 template<int i, int j> pendant la compilation. 8 struct Multiply Developpez Magazine est une publication de Developpez.com page 2 Numéro 57 avril–mai 2015 1 int array[ Multiply<3, 4>::value ]; // 10 static const int value = un array NegativeArgument<i, (i >= 0)>:: value; LaformuleMultiply<3,4>::valueressembledif- 11 }; ficilementàunappeldefonction,maisc’enestpour- Prenons l’exemple en partant de la fin. La mé- tant un : vous entrez des valeurs et vous obtenez un tafonction SafeFactorial transmet l’argument à une résultat. autre métafonction : NegativeArgument, mais elle C’est ici que les questions intéressantes com- passeaussiparlaprécondition(i>=0),pouréviter mencent à arriver... les cas où i est négatif. Savez-vousquechaquemétafonctionestdéclarée Le nom de la métafonction suivante peut sem- comme une classe artificielle et qu’elle est évaluée bler obscur à ce stade, mais il vous paraîtra beau- en instanciant une classe template et en accédant à coup moins abstrait quand il s’agira de générer des un membre statique? Si vous travaillez depuis un messages d’erreur à la compilation. moment avec les métafonctions et que vous êtes ha- bitué(e) à leur syntaxe, vous pouvez ne pas réaliser La clé de voûte de cet exemple se trouve dans que ça puisse être un problème. lamétafonctionNegativeArgument.Laspécification Sivouspensezquecepremierexempleestsimple, pour le booléen à false est déclarée explicitement passons à un exemple un peu plus difficile. (pourintercepterunnon-respectdelaprécondition) mais laissée indéfinie pour être sûr que le code qui Calculons une factorielle, pour voir. Je ne pense utilisecettespécialisationvaprovoqueruneerreurà pas que quiconque ait besoin de calculer une fac- la compilation. S’il y a une erreur lors de la compi- torielle durant la compilation, mais c’est l’exemple lation, nous allons avoir un message d’erreur disant le plus accessible que je puisse vous présenter pour quelquechosecomme«utilisationdetypenondéfini illustrer les concepts de base de la programmation NegativeArgument<i,c>».Cen’estpasunmessage fonctionnelle : la récursivité, les conditions d’arrêt parfaitementexplicite,maisjel’espèresuffisamment de récursivité et la gestion des erreurs. clair pour vous donner une idée de ce qui ne va pas. 1 template<int i> struct Factorial La version à deux paramètres de NegativeArgu- 2 { mentvadirectementtransmettrel’argumentànotre 3 static const int value = i * Factorial<i - 1>::value; bonne métafonction Factorial. 4 }; Le point que j’essaie de mettre en lumière ici est 5 queladéfinitiondemétafonctionscommecelle-làest 6 template<> struct Factorial<0> un peu compliquée, voire ésotérique dans certains 7 { 8 static const int value = 1; cas. 9 }; Comment C++11 va-t-il nous aider? En éten- La première définition introduit la récursion. La dant le concept des expressions constantes. Vous valeur value de l’instanciation courante est détermi- avezprobablementdéjàentenduparlerdeconstexpr néeparlavaleurcorrespondanted’uneautreinstan- et ce qu’il vous permet de faire. ciation.Lasecondedéfinitioncorrespondàlacondi- Pour faire court, en C++11, nos deux versions tion finale de récursivité, pour i == 0. (sécurisée et non sécurisée) du calcul de factorielle Uneautrechoseàvoir,c’estlavérificationd’une durantletempsdecompilationpeuventêtredéfinies préconditionpoursignaleruneerreurlecaséchéant. ainsi : Danslecasdelafactorielle,onveutéliminerlapos- 1 constexpr int factorial(int i) sibilité d’avoir des arguments négatifs. Bien sûr, on 2 { aurait pu utiliser le type unsigned int pour éviter le 3 return (i == 0 ) ? // problème,maislebutdecetexerciceestdemontrer condition terminale commentlavérificationd’unepréconditionpeutêtre 4 1 : // et valeur terminale implémentée en général. 5 i * factorial(i - 1); // dé Çanécessitededéfinird’autresmétafonctionsen finition de la récursivit amont de ce bloc vulnérable. é 6 } 1 template<int i, bool c> struct 7 NegativeArgument 8 constexpr int safe_factorial(int i) 2 { 9 { 3 static const int value = Factorial<i 10 return (i < 0) ? // >::value; condition d’erreur 4 }; 11 throw exception() : // géné 5 ration d’erreur ( 6 template<int i> struct NegativeArgument< compilation) i, false>; //indéfini 12 factorial(i) ; // vrai 7 calcul 8 template<int i> struct SafeFactorial 13 } 9 { Developpez Magazine est une publication de Developpez.com page 3 Numéro 57 avril–mai 2015 Une fonction constexpr est presque comme une définir et que cet exemple tient facilement dans cet fonction normale, mais nous devons utiliser un opé- article. Vous n’aurez probablement jamais de votre rateurdeconditionplutôtqu’unif.Grâceàcetoutil, vieàcalculerunefactoriellependantlacompilation. nous pouvons déclarer un tableau de 24 éléments de Mais il est tout à fait possible de devoir calculer un cette façon : jour le plus grand commun diviseur si on souhaite implémenterunebibliothèquedenombresrationnels 1 int array[ factorial(4) ]; durant la compilation. Encore une fois, j’ai choisi un exemple de calcul de factorielle, car c’est une opération très simple à 4 Calcul de types Un autre type de métafonction est celui où nous Vouspouvezavancerqu’uncompilateurdoitêtre allons passer un type comme argument et en avoir assez intelligent pour pouvoir faire ce travail sans unautreenretour.Pourexemple,essayonsd’implé- l’aide du programmeur, mais ce n’est pas facile menter la fonction remove_pointer (comme celle de à implémenter en général et... c’est simplement la STL) qui, pour les types U = T*, retourne T et ainsi que le C++ fonctionne. À la fin, la perspec- qui, pour tous les autres types, retourne le même tive d’avoir à écrire du code comme typename re- type inchangé. Autrement dit, la métafonction sup- move_pointer<T> : :type est peu attirante et rend prime le pointeur de plus haut niveau là où il est le code difficile à lire, surtout pour vos collègues qui possible de l’enlever. ne veulent pas être embêtés par ce genre de chose, C’estpossiblegrâceàunespécialisationpartielle mais tel est le C++03. de template : Heureusement, le C++11 offre un certain confort : les alias de templates (lien 4). Les alias de 1 template<typename U> // en général type sont une nouvelle forme pour définir des types, 2 struct remove pointer 3 { un peu à la manière de typedef, mais avec une syn- 4 typedef U type; taxe améliorée : 5 }; 6 1 using Integer = int; 7 template<typename T> // pour U = T* 2 // même chose que : typedef int Integer; 8 struct remove_pointer<T*> 3 9 { 4 using FunPtr = int* (*)(int*); 10 typedef T type; 5 // même chose que : typedef int*(*FunPtr 11 }; ) (int*); Celanesemblepassimal,maisdansl’étatactuel Les alias de templates ajoutent une fonctionna- deschoses,pourutilisernotremétafonctiondansun lité qui manquait depuis longtemps au C++03 : template de fonction, vous devrez utiliser une syn- taxe quelque peu étrange : 1 template<typename T> 2 using StackVector = std::vector<T, 1 template<typename T> StackAllocator<T>>; 2 typename remove_pointer<T>::type fun(T 3 val); 4 StackVector<int> v; La nécessité d’utiliser le peu pratique typename Avec les alias de templates, nous pouvons écrire est une conséquence des règles de la spécialisa- notremétafonctiond’effacementdepointeurcomme tion partielle des classes template. Le compilateur suit : a besoin d’être préparé aux vilaines spécialisations comme celle qui suit : 1 template<typename U> // en géné ral 1 template<typename U> // template ma 2 struct remove_pointer ître 3 { 2 struct MyClass 4 using type = U; 3 { 5 }; 4 typedef U type; // définition 6 d’un type 7 template<typename T> // pour U = 5 }; T* 6 8 struct remove_pointer<T*> 7 template<typename T> // spé 9 { cialisation 10 using type = T; 8 struct MyClass<const T> 11 }; 9 { 12 10 static int type = 0; // définition 13 template<typename W> d’un objet 14 using RemovePointer = typename 11 }; remove_pointer<W>::type; Developpez Magazine est une publication de Developpez.com page 4 Numéro 57 avril–mai 2015 La définition de notre métafonction n’est pas 2 RemovePointer<T> fun(T val); pluscourte.Aussi,nousavonstoujoursbesoind’uti- liser l’horrible typename. Cependant, la façon dont Cette technique pour noter les métafonctions a lamétafonctionestutiliséeaétéamélioréesignifica- étéemployéedanslerapport«AConceptDesignfor tivement : the STL » par B. Stroustrup et A. Sutton (lien 5) ainsi que dans les bibliothèques Origin (lien 6). 1 template<typename T> 5 Essayez vous-même Il y a plusieurs fonctionnalités qui rendent la plates récursifs). J’ai seulement listé les deux que métaprogrammation plus simple en C++11, les as- je trouvais les plus intéressantes. Les expressions sertions statiques en font partie (lien 7). Quelques constantes généralisées (constexpr) sont implémen- évolutions viennent simplement de l’amélioration tées dans GCC depuis la version 4.6 tout comme les des implémentations par le compilateur (à savoir alias de templates ont été introduits dans GCC 4.7 un meilleur support pour l’instanciation des tem- et Clang 3.0. Retrouvez l’article de Andrzej Krzemieński traduit parGuy Arbus en ligne : lien 8 Developpez Magazine est une publication de Developpez.com page 5 Numéro 57 avril–mai 2015 JavaScript Les derniers tutoriels et articles Le jeu Mario5 avec TypeScript De JavaScript en TypeScript, la conversion par l’exemple Revue des principaux axes de travail pour convertir un code JavaScript en code TypeScript idiomatique. 1 Avant-propos Ce qui suit est une traduction d’un article de sera abordé ici, mais uniquement la conversion en Florian Rappl dont le but premier est de montrer TypeScript à proprement parler. les principaux axes de travail pour transformer du Bien qu’il puisse être utile de lire au préalable code JavaScript en code TypeScript idiomatique. l’articledédiéaudéveloppementdujeuMario5,cela Pour cela, l’auteur se base sur un de ses projets, reste facultatif dans la mesure où les divers aspects le jeu Mario5 initialement écrit en JavaScript et sur de la conversion sont illustrés à la fois par du code lequeliladéjàécritunarticleconcernantlaconcep- JavaScript issu du jeu initial et par du code TypeS- tion et l’implémentation du jeu. Ce n’est pas ce qui cript résultant de la conversion. 2 Introduction Encequimeconcerne,l’undesmomentslesplus prochainement de ES6. Bien sûr, TypeScript trans- mémorables sur CodeProject a été la publication de pile en ES3 ou ES5, ce qui signifie que les classes l’article sur Mario5 : lien 9. Dans l’article, je décri- serontdécomposéesenquelquechosequiestaccepté vais la réalisation d’un jeu basé sur les technologies dès maintenant : les prototypes. Néanmoins, ce qui Web comme HTML5, CSS3 et JavaScript. L’article reste est un code qui est lisible, compatible ES3 ou a eu pas mal de succès et est probablement l’un de ES5, fiable et partageant une base commune. Avec ceux dont je suis vraiment fier. mon approche spécifique (oop.js), personne d’autre L’articleoriginalutilisequelquechosequej’aidé- que moi ne pouvait dire ce qu’il se passait sans lire critcommedu«JavaScriptorientéobjet».J’aiécrit le code de ma bibliothèque auxiliaire. Avec TypeS- unepetitebibliothèqueauxiliaireappeléeoop.js,qui cript, un large éventail de développeurs utilisent le m’apermisd’utiliserl’héritageinspirédumodèledes même modèle, car il est intégré dans le langage. classes. Évidemment, JavaScript est très orienté ob- jet depuis ses débuts. Les classes ne sont pas uni- Celacoulaitdoncdesourcedeconvertirleprojet quement une caractéristique essentielle de la POO. Mario5enTypeScript.Pourquoienfaireunarticle? Néanmoins,cemodèlem’aététrèsutilepourrendre Je pense que c’est un excellent cas pratique sur la le code à la fois, facile à lire et à maintenir. Et fina- manière de convertir un projet. Il illustre également lement, ceci permet de ne pas avoir à traiter direc- les principaux aspects de TypeScript. Et enfin, il tement avec le concept de prototype. donne une bonne introduction à sa syntaxe et à son Avec TypeScript nous avons à disposition une comportement. Après tout, TypeScript est simple construction normalisée des classes en JavaScript. pourceuxquiconnaissentdéjàJavaScriptetfacilite La syntaxe est basée sur la version ES6, faisant de l’apprentissagedeJavaScript,pourceuxquin’enont TypeScript un surensemble de JavaScript ES5 et pas encore l’expérience. Developpez Magazine est une publication de Developpez.com page 6 Numéro 57 avril–mai 2015 3 Contexte Ilyaplusd’unan,AndersHejlsbergdechezMi- plicites,etc.découlentdecesannotationsdetype.À crosoftannonçaitunnouveaulangageappeléTypeS- l’avenir, TypeScript évoluera. Principalement dans cript.Ilétaitsurprenantpourlaplupartdesgensque deux domaines : Microsoft (et surtout Anders) aille à l’encontre des 1. Englober ES6 afin de rester un véritable sur- langages dynamiques, en particulier JavaScript. Ce- ensemble de JavaScript; pendant, il s’est avéré que Microsoft a compris l’op- portunité que représentait sa transition de la pro- 2. Apporter de nouvelles fonctionnalités pour grammation à usage général vers la programmation rendre plus facile le développement en JS. Web. Avec JavaScript au sein des applications Win- dows Store, l’engouement actuel pour node.js et le Il y a principalement deux avantages à l’utili- mouvementNoSQLaveclesbasesdedonnéesorien- sation de TypeScript. Le premier aspect est que téesdocumentsutilisantJavaScriptpourl’exécution nous pouvons être informé des erreurs et des pro- derequêtes,ilestévidentqueJavaScriptestaujour- blèmes potentiels lors de la compilation. Si un ar- d’hui central. gument n’est pas conforme à sa signature, alors le Avoircompriscelaainfluencélaconceptiond’un compilateur renvoie une erreur. Cela est particuliè- nouveau langage. Au lieu de créer un nouveau lan- rementutilelorsquevoustravaillezavecdeséquipes gage à partir de zéro (comme Google l’a fait avec ou des projets de taille importante. Le second as- Dart),Andersadécidéqu’unnouveaulangagesede- pectestégalementintéressant.Microsoftestconnue vaitd’étendreJavaScript.Aucunesolutionnedevant pour son outillage de très bonne facture avec Vi- être orthogonale. Le problème avec CoffeeScript est sual Studio. Mais fournir un bon outillage au lan- qu’il masque JavaScript. Cela peut être attrayant gage JavaScript est délicat en raison de sa nature pour certains développeurs, mais pour la plupart dynamique. Par conséquent, la moindre refactorisa- d’entre eux, c’est un critère d’exclusion absolu. An- tion, même simple, comme renommer une variable, ders a décidé que ce langage devait être fortement peut ne pas être effectuée avec la fiabilité souhaitée. typé, même si seul le compilateur (ou transpileur TypeScript nous fournit un support important pour être plus correct) voit ces annotations. au niveau de l’outillage combiné avec une bien Qu’est-il donc arrivé? Un véritable surensemble meilleure compréhension sur la façon dont notre de ECMAScript 5 a été créé. Ce surensemble a code va fonctionner. La combinaison de la produc- été appelé TypeScript pour indiquer son lien étroit tivité et de la robustesse est l’argument le plus at- avec JavaScript (ou ECMAScript en général), avec trayant pour utiliser TypeScript. Dans cet article, des annotations supplémentaires de type. Toutes les nous allons explorer comment convertir un projet autresfonctionnalités,commelesinterfaces,lesénu- existant.Nousallonsvoirquelatransformationd’un mérations, les types génériques, les conversions ex- code en TypeScript peut se faire progressivement. 4 Convertir un projet existant TypeScript ne cache pas le JavaScript. Il com- nécessaire. Néanmoins, comme le compilateur Ty- mence à partir du JavaScript de base. peScript tsc considère généralement en entrée les fichiers *.ts , et en sortie des fichiers *.js, renom- mer l’extension garantit que rien de fâcheux ne se passera. Les prochains paragraphes traitent des amélio- rations progressives dans le processus de conver- sion. Nous supposons à présent que chaque fichier a l’extension TypeScript usuelle *.ts, même si aucune fonctionnalité spécifique à TypeScript n’est encore utilisée. La première étape dans l’utilisation de TypeS- 4.1 Référencement cript est bien sûr d’avoir des fichiers source Ty- peScript. Puisque nous voulons utiliser TypeScript La première étape est de fournir dans un fichier dansunprojetexistant,nousdevonsconvertircesfi- JavaScript les références de tous les autres fichiers chiers. Il n’y a pas de condition préalable si ce n’est JavaScript qui lui sont nécessaires. Habituellement, de renommer nos fichiers *.js en *.ts. C’est juste nousn’avonsqu’àgérerdesfichiersindépendantsqui unequestiondeconvention,quin’estpasréellement cependant (généralement) doivent être insérés dans Developpez Magazine est une publication de Developpez.com page 7 Numéro 57 avril–mai 2015 uncertainordredansnotrecodeHTML.Lesfichiers Engénéral,iln’estpasrecommandéd’êtreexpli- JavaScript ne connaissent pas le fichier HTML, ils cite sur ces annotations. Cela encombre inutilement ne connaissent pas leur ordre dans le fichier HTML le code et le rend moins souple. Cependant, parfois, (sansmêmeparlerdesavoirquelsfichiersJavaScript ces annotations sont nécessaires. Bien sûr, le cas le sont insérés). plusévidentestlorsquenousnefaisonsquedéclarer Comme nous voulons donner quelques indica- une variable : tions à notre compilateur intelligent (TypeScript), 1 var frameCount: number; nous devons préciser que d’autres éléments peuvent être disponibles. Par conséquent, nous devons ajou- Il y ad’autres scénarios. Envisageons la création ter des références au début des fichiers source. Ces d’unobjetsimplepouvantêtreétenduavecdespro- référencesprécisenttouslesautresfichiersquiseront priétéssupplémentaires.LecodeJavaScripthabituel utilisés dans le fichier en cours. necontientpasassezd’informationpourlecompila- Parexemple,nouspourrionsinclurejQuery(uti- teur : lisé par exemple dans le fichier main.ts) par sa défi- 1 var settings = { }; nition via : Quelles sont les propriétés disponibles? Quel est 1 /// <reference path="def/jquery.d.ts"/> le type de ces propriétés? Peut-être que nous ne le savons pas et nous voulons utiliser cet objet comme Nous pourrions également inclure une version undictionnaire.Danscecas,nousdevrionsspécifier TypeScriptdelabibliothèqueoubiensaversionJa- le cadre d’utilisation de cet objet : vaScript même s’il est préférable de n’inclure que le fichier de définition. Les fichiers de définition ne 1 var settings: any = { }; contiennent aucune logique. Cela rend ces fichiers Mais il y a aussi un autre cas. Nous savons déjà significativementpluspetitsetplusrapidesàanaly- quelles propriétés peuvent être disponibles, et nous ser. En outre, ces fichiers contiennent généralement avons seulement besoin de définir ou de récupérer davantagedecommentairesetsontdemeilleurequa- une partie de ces propriétés optionnelles. Dans ce lité. Enfin, alors que nous pourrions préférer nos cas, on peut tout à fait spécifier le type exact : propres fichiers *.ts aux fichiers *.d.ts, il faut savoir que pour le cas de jQuery et pour d’autres biblio- 1 var settings: Settings = { }; thèques, elles sont à l’origine écrites en JavaScript Lecasleplusimportantaétéomisjusqu’ici.Tan- et auront besoin d’un fichier de définition pour que dis que les variables (locales ou globales) peuvent tout fonctionne correctement. êtredéduitesdanslaplupartdescas,lesparamètres Nous pouvons être amenés à décrire par nous- d’une fonction ne peuvent jamais être déduits. En mêmes des fichiers de définition simples. L’exemple touterigueur,lesparamètresd’unefonctionpeuvent le plus basique est le fichier def/interfaces.d.ts. être déduits dans une seule situation (comme avec Celui-ci ne contient pas de code à proprement par- lestypesdeparamètresgénériques),maispasdansla ler, par conséquent une compilation de ce fichier fonction elle-même. Nous devons donc dire au com- serait inutile. Le référencement de ce fichier, par pilateur le type des paramètres que nous avons. contre, est logique, puisque les informations supplé- mentaires fournies sur les types par ce fichier nous 1 setPosition(x: number, y: number) { this .x = x; this.y = y; } aident à annoter notre code. Transformer JavaScript de façon incrémentale avec les annotations de type est donc un processus 4.2 Annotations qui commence par la mise à jour de la signature des La caractéristique la plus importante de TypeS- fonctions.Danscecas,quellessontlesbasesconcer- cript est les annotations de type. En fait, le nom nant ces annotations? Nous avons déjà appris que de ce langage indique la grande importance de cette number, string et any sont des types primitifs, qui fonctionnalité. représentent des types élémentaires. De plus, nous La plupart des annotations de type ne sont pas avonsbooleanetvoid.Cederniern’estutilequepour réellement nécessaires. Si une variable est immédia- le type de retour des fonctions. Il indique que rien tement affectée (nous définissons une variable, au d’utile n’est retourné (les fonctions JS retournent lieu de simplement la déclarer), alors le compilateur toujours quelque chose, par défaut undefined). peut déduire le type de la variable. Qu’en est-il des tableaux? Un tableau standard est de type any[]. Si nous voulons indiquer que 1 var basepath = ’Content/’; seuls les nombres peuvent être utilisés avec ce ta- Évidemment, le type de cette variable est une bleau, nous pourrions l’annoter number[]. Les ta- chaîne (string). C’est aussi ce que déduit TypeS- bleaux multidimensionnels sont aussi possibles. Une cript. Néanmoins, on pourrait aussi mentionner le matricepeutêtreannotéecommenumber[][].Enrai- type explicitement. sondelanaturedeJavaScript,nousn’avonsquedes tableaux irréguliers pour les dimensions multiples. 1 var basepath: string = ’Content/’; Developpez Magazine est une publication de Developpez.com page 8 Numéro 57 avril–mai 2015 4.3 Énumérations 4.4 Interfaces Maintenant que nous avons commencé à anno- Les interfaces définissent des types sans spécifier ter nos fonctions et nos variables, il va nous fal- leur implémentation. Elles disparaîtront complète- loir des types personnalisés. Bien sûr, nous avons mentdanslecodeJavaScriptrésultant,toutcomme déjà quelques types ici et là. Cependant, ces types nos annotations de type. Essentiellement, elles sont peuvent ne pas être aussi précis que nous le souhai- assez semblables aux interfaces en C#, cependant, terions,oubienêtredéfinisdefaçontropspécifique. il y a quelques différences notables. Examinons une interface de notre code : TypeScript peut offrir de meilleurs choix. Les collections de constantes numériques, par exemple, 1 interface LevelFormat { width: number; peuventêtredéfiniescommeuneénumération.Dans height: number; id: number; l’ancien code nous avions des objets tels que : background: number; data: string [][]; } 1 var directions = { none: 0, left: 1, up: Cela définit le format d’une définition d’un ni- 2, right: 3, down: 4 }; veau du jeu. Nous voyons qu’une telle définition consiste en des nombres tels la largeur (width), la Il n’est pas évident que les éléments contenus hauteur (height), l’arrière-plan (background) et un soient des constantes. Ils pourraient facilement être identifiant(id).Deplus,unechaînebidimensionnelle modifiés. Un compilateur ne pourrait-il pas générer (data) définit les différentes tuiles qui doivent être une erreur si nous faisions des choses inappropriées utilisées dans le niveau. avec un tel objet? C’est là que le type enum est Nous avons déjà mentionné que les interfaces en utile. Actuellement, ils sont limités à des nombres, TypeScriptsontdifférentesdecellesenC#.Unedes cependant, pour la plupart des collections cela est raisons est que TypeScript autorise la fusion des in- suffisant. Plus important encore, ils sont considérés terfaces. Si une interface a le nom d’une interface comme des types, ce qui signifie que nous pouvons déjàexistante,celle-cineserapasécrasée.Iln’yaura les utiliser dans nos annotations de type. aucun avertissement du compilateur ni erreur. Au Une majuscule a été ajoutée en début de nom, lieu de cela, l’interface existante sera étendue avec ce qui indique que Direction est en effet un type. les propriétés définies dans la nouvelle. Puisque nous ne voulons pas l’utiliser comme une L’interfacesuivantefusionneavecl’interfacepré- énumération en mode binaire, nous utilisons la ver- existante Math (issue des définitions de base de Ty- sion simple (par rapport à la convention de .NET, peScript). Ajoutons une nouvelle méthode : ce qui est logique dans ce scénario). 1 interface Math { sign(x: number): number 1 enum Direction { none = 0, left = 1, up ; } = 2, right = 3, down = 4, }; Les méthodes sont déclarées en spécifiantlespa- Maintenant, nous pouvons l’utiliser dans le code ramètres entre parenthèses. Communément, l’anno- ainsi : tationdetypeconcerneleretourdelaméthode.Avec l’interfacequenousvenonsd’étendre,lecompilateur 1 setDirection(dir: Direction) { this. de TypeScript nous permet d’écrire la méthode sui- direction = dir; } vante : Notezqueleparamètredirestannotédemanière 1 Math.sign = function(x: number) { if (x à restreindre les arguments au type Direction. Cela > 0) return 1; else if (x < 0) exclut les nombres quelconques et impose d’utiliser return -1; return 0; }; les valeurs de l’énumération Direction. Que faire si Une autre option intéressante avec les interfaces nous avons une entrée de l’utilisateur qui se trouve TypeScript est la déclaration hybride. En JavaS- êtreunnombre?Dansuntelscénario,nouspouvons cript, un objet n’est pas limité à être un simple également forcer le type et utiliser une conversion transporteur de paires clé-valeur. Un objet peut explicite (cast) TypeScript : aussi être invoqué comme une fonction. Un bon exemple d’un tel comportement est jQuery. Il y a 1 var userInput: number; // ... plusieurs façons possibles d’appeler l’objet jQuery, setDirection(<Direction>userInput); chacune d’entre elles retournant une nouvelle sélec- LesconversionsexplicitesenTypeScriptnefonc- tion jQuery. De plus, l’objet jQuery comporte éga- tionnent que si elles sont légales. Étant donné que lement des propriétés qui se révèlent être des outils chaque Direction est un nombre, un certain nombre tout à fait intéressants et utiles. pourrait être une Direction valide. Parfois, une Dans le cas de jQuery, une des interfaces res- conversion explicite peut à l’avance être détectée semble à ceci : commeinvalide.SiuserInputestdetypestring,Ty- 1 interface JQueryStatic { (): JQuery; ( peScript se plaint et retourne une erreur de conver- html: string, ownerDocument?: sion. Document): JQuery; ajax(settings: Developpez Magazine est une publication de Developpez.com page 9 Numéro 57 avril–mai 2015 utiliser et standard. Si nous revenons sur l’exemple JQueryAjaxSettings): JQueryXHR; /* ... */ } initial, nous pouvons le transformer ainsi : Ici, nous avons deux appels possibles (parmi 1 class Gauge extends Base { constructor( d’autres) et une propriété qui est directement mise id: string, startImgX: number, àdisposition.Lesinterfaceshybridesnécessitentque startImgY: number, fps: number, l’implémentation soit une fonction enrichie d’autres frames: number, rewind: boolean) { super(0, 0); this.view = $(’#’ + id) propriétés. ; this.setSize(this.view.width(), On peut aussi créer des interfaces basées sur this.view.height()); this.setImage( d’autres interfaces (ou des classes, qui seront uti- this.view.css(’background-image’), lisées comme interfaces dans ce contexte). startImgX, startImgY); this. setupFrames(fps, frames, rewind); } Prenons le cas suivant. Pour caractériser les }; points, nous utilisons l’interface Point. Ici, nous dé- C’est relativement similaire et cela se comporte clarons deux coordonnées x et y. Pour définir une de façon à peu près identique. Cependant, le rem- image, nous avons besoin de deux valeurs. Un em- placementdel’implémentationprécédenteparlava- placement (offset), où l’image doit être placée, et riante TypeScript doit être réalisé en une seule ité- la chaîne de caractères qui représente la source de ration. Pourquoi? Si nous changeons la classe de l’image. base (appelé simplement Base), nous devons chan- Par conséquent, nous pouvons définir une inter- gertouteslesclassesdérivées(lesclassesTypeScript facecommeétantladérivation/spécialisationdel’in- doivent hériter d’autres classes TypeScript). terfacePoint.Lemot-cléextendspermetcelaenTy- peScript. D’autre part, si nous changeons l’une des classes dérivées nous ne pouvons plus utiliser la classe de 1 interface Point { x: number; y: number; base. Cela étant dit, seules les classes, qui sont } interface Picture extends Point { complètementdécoupléesd’unehiérarchiedeclasse, path: string; } peuvent être transformées en une seule itération. Nous pouvons faire dériver d’autant d’interfaces Dans le cas contraire, nous devons mettre à jour que nous le souhaitons, il suffit pour cela de séparer l’ensemble de la hiérarchie de classe en une seule les noms des interfaces par des virgules. fois. Le mot-clé extends a une signification différente 4.5 Classes de celle pour les interfaces. Une interface étend Àcestade,nousavonsdéjàtypélamajeurepar- d’autres définitions (interface ou la partie interface tie de notre code, mais un concept important n’a d’une classe) par un ensemble de nouvelles défini- pas été traduit en TypeScript. Le code source d’ori- tions. Une classe étend une autre classe en appli- gine introduit le concept de classe (y compris l’hé- quant son prototype à la classe étendue. En outre, ritage) dans JavaScript. Au départ, cela ressemblait d’autres possibilités sont offertes, comme la possibi- à l’exemple suivant : lité d’accéder aux méthodes de la classe parente par l’intermédiaire de super. 1 var Gauge = Base.extend({ init: function (id, startImgX, startImgY, fps, La classe la plus importante est la racine de la frames, rewind) { this._super(0, 0); hiérarchiedeclasses,appeléeBase.Ellecontientpas this.view = $(’#’ + id); this. mal de fonctionnalités. setSize(this.view.width(), this.view .height()); this.setImage(this.view. 1 class Base implements Point, Size { css(’background-image’), startImgX, frameCount: number; x: number; y: startImgY); this.setupFrames(fps, number; image: Picture; width: frames, rewind); }, }); number; height: number; currentFrame Malheureusement, il y a beaucoup de problèmes : number; frameID: string; avec cette approche. Le plus gros problème est rewindFrames: boolean; frameTick: number; frames: number; view: JQuery que cette approche n’est pas normative, c’est-à-dire ; constructor(x: number, y: number) qu’elle n’est pas standard. Par conséquent, les dé- { this.setPosition(x || 0, y || 0); veloppeurs qui ne sont pas habitués à cette implé- this.clearFrames(); this.frameCount mentation des classes peuvent avoir des difficultés = 0; } setPosition(x: number, y: number) { this.x = x; this.y = y; } à lire ou à écrire un tel code, contrairement à ce getPosition(): Point { return { x : qu’ils peuvent avoir l’habitude de rencontrer. De this.x, y : this.y }; } setImage(img plus, l’implémentation exacte est cachée. Pour la : string, x: number, y: number) { connaître, le développeur doit regarder la définition this.image = { path : img, x : x, y : y }; } setSize(width, height) { originaledelaclasseetlafaçondontelleestutilisée. this.width = width; this.height = Avec TypeScript, il existe une façon unifiée de height; } getSize(): Size { return { créer des classes. En outre, l’implémentation est ali- width: this.width, height: this. height }; } setupFrames(fps: number, gnéesurECMAScript6.Parconséquent,nousobte- frames: number, rewind: boolean, id nons une façon portable, lisible, extensible, facile à ?: string) { if (id) { if (this. Developpez Magazine est une publication de Developpez.com page 10 Numéro 57 avril–mai 2015
Description: