1. Livres & vidéos
  2. JavaScript
  3. TypeScript
Extrait - JavaScript Développez efficacement (5e édition)
Extraits du livre
JavaScript Développez efficacement (5e édition) Revenir à la page d'achat du livre

TypeScript

Introduction

1. Objectifs

JavaScript est particulièrement malléable comme nous avons pu le voir, mais il nécessite un effort de programmation pour trouver les bons usages. La société Microsoft a mis au point un sur-ensemble de JavaScript appelé TypeScript. Quand on parle de sur-ensemble, cela signifie que JavaScript est inclus dans TypeScript qui ajoute ce qui semble manquer au langage comme le typage de données. Un de ses avantages est l’étape de compilation de TypeScript qui consiste à obtenir un code JavaScript compatible avec les navigateurs.

2. Hello world

Nous allons partir de la forme la plus simple en JavaScript pour réaliser notre premier exemple. L’idée est d’obtenir le compilateur TypeScript via le gestionnaire de package de Node.js (NPM).

Si vous ne disposez pas de l’utilitaire NPM, vous pouvez le télécharger sur le site de nodejs.org (https://nodejs.org/en/download/).

La commande suivante sert à installer le compilateur TypeScript :

npm install -g typescript 
images/07ei01.png

Nous allons maintenant créer un fichier TypeScript (extension .ts) en utilisant uniquement du JavaScript pour l’instant.

exemple1.ts

function hello() { 
  alert( "hello world" ); 
} 
hello(); 

Pour le compiler, nous utilisons simplement la commande :

tsc exemple1.ts 

Ce qui produit un fichier exemple1.js. Ce fichier est strictement...

Typage des expressions

1. Types disponibles

TypeScript reprend la même syntaxe qu’avec nos usages de variables et constantes. Ce qui change est la nécessité d’indiquer un type lors de la déclaration. Si vous utilisez Visual Studio Code, il vous affichera lors de la saisie toutes les erreurs de type.

Voici tous les types primitifs disponibles :

  • boolean : une valeur booléenne.

  • number : un nombre à virgule.

  • bigint : un nombre entier.

  • string : une chaîne de caractères.

À cela s’ajoutent des types particuliers comme :

  • [] : associé à un type pour désigner un tableau.

  • tuple : pour désigner un tableau avec des types à un indice donné.

  • enum : énumération de valeur associée automatiquement à un entier.

  • unknown : pour un type inconnu.

  • any : pour demander à TypeScript d’ignorer le type.

  • void : désigne une fonction qui ne retourne pas de valeur.

  • undefined : pour une valeur non affectée.

  • null : pour l’absence d’une référence objet.

  • never : désigne une fonction qui ne se termine pas ou provoque une exception.

  • objet : désigne un objet.

2. Déclarations

Le type d’une valeur est précisé par « : » suivi du nom de type en question. Par exemple :

let nom : string = "brillant";  
let prenom : string = "alexandre";  
const pi : number = 3.14;  
const pis : number[] = [ pi, 2*pi , 3*pi ];  
   
type Personne = {  
    nom : string,  
    prenom : string  
};  
   
const monObjet : Personne = {  
    nom : nom,  
    prenom : prenom  
};  
  
console.log( `Bonjour ${monObjet.nom} ${monObjet.prenom}`  ); 

Dans notre exemple, nom et prenom sont des chaînes de caractères (string). pis est un tableau de nombres. Pour un objet littéral, il faut définir chaque type de chaque propriété. Nous avons créé pour cela un type Personne avant l’affectation de valeurs.

Cela sera la même chose pour typer une fonction avec ces paramètres et éventuellement sa valeur...

Classes

1. Déclaration et usage

Le concept de classe n’est pas très éloigné de ce que nous avons déjà vu en JavaScript « traditionnel ». On va surtout y ajouter les notions de typage concernant les attributs et les méthodes.

class Personne {  
    nom:string;  
    prenom:string;  
  
    constructor( nom :string, prenom:string ) {  
        this.nom = nom;  
        this.prenom = prenom;  
    }  
   
    bonjour():void {  
        console.log( `Bonjour ${this.nom} ${this.prenom}` );  
    }  
}  
const auteur : Personne = new Personne( "brillant", "alexandre" );  
   
auteur.bonjour(); // « bonjour brillant alexandre » 

À noter que la syntaxe est plus rigoureuse qu’en JavaScript traditionnel car le fait de laisser des attributs non initialisés par le constructeur est considéré comme étant une erreur.

Étrangement, il n’est possible de n’avoir qu’un seul constructeur, par contre la définition de ce constructeur peut être multiple. Tout comme pour les arguments d’une fonction, nous pouvons déclarer un attribut optionnel avec l’opérateur « ? ».

class Personne {  
    nom:string;  
    prenom:string; 
    age?:number;  
  
    constructor( nom:string, prenom:string );  
    constructor( nom : string,prenom:string,age:number );  
    constructor( nom :string, prenom:string,age?:number ) {  
        this.nom = nom;  
        this.prenom = prenom;  
        if ( age !== undefined )  
            this.age = age;  
    }  
  
    bonjour():void {  
        console.log( `Bonjour...

Interfaces

1. Déclaration

JavaScript, tout comme TypeScript, n’autorise pas d’héritage multiple, cela veut dire qu’une classe ne doit avoir qu’un seul parent possible. Cela est un choix des concepteurs du langage pour éviter toute ambiguïté car des propriétés/attributs/méthodes pourraient être présents avec un même nom dans deux classes parentes différentes.

Cependant, il peut arriver que l’on ait besoin de gérer des objets qui n’ont pas forcément de relation d’héritage évidente et/ou possible. Dans ce cas, on va essayer de manipuler tous ces objets par une sorte de contrat que l’on nomme interface.

C’est un concept que l’on trouve dans d’autres langages comme Java ou C#. L’interface désigne les signatures que l’on s’attend à trouver dans un ensemble de classes. Les classes n’ont pas besoin d’être en relation, elles doivent juste respecter l’interface et garantir que certaines signatures de méthodes sont bien présentes.

L’usage n’est pas forcément évident mais s’avère pratique en réalité pour simplifier le code. Imaginer par exemple que vous ayez un service qui emballe dans des cartons toutes sortes d’objets. Ces objets doivent avoir une certaine taille, mais pourtant ces objets...

Génériques

1. Déclaration en fonction

Jusqu’ici, nous avons employé des fonctions ou des classes avec des arguments ou des membres ayant un type bien précis. Cela signifie que nous ne pouvons pas réutiliser ces fonctions et classes en dehors de ces types. Or, il existe des cas où nous voudrions pouvoir manipuler nos fonctions/classes avec des types différents. C’est ce que les « génériques » nous proposent de faire.

C’est un principe que l’on retrouve souvent dans d’autres langages comme Java, C# ou C++.

Nous allons commencer par écrire une fonction générique, le principe est d’ignorer un ou plusieurs types et de le préciser comme une forme de paramètre entre crochets.

function maxVal<T>(args:T[], comparer : (a:T,b:T) => number ) : T { 
    return args.reduce(  
        (accumulateur:T,valeur:T)=>  
            comparer(accumulateur,valeur) > 0 ? accumulateur : 
valeur );  
   
}  
   
const monTableau = [1,2,3,4,5,6];  
   
console.log( maxVal(monTableau, (a,b)=> a - b ) );  
  
const monTableau2 = ["aaaaa", "zzzz"];  
   
console.log( maxVal( monTableau2, (a,b)=>a.localeCompare(b) )   

Dans cet exemple, nous avons une fonction générique maxVal qui prend en argument un tableau et retourne sa plus grande valeur quel que soit le type utilisé....

Espace de noms

Déclaration et usage

L’espace de noms avec le mot-clé namespace va « héberger » toutes les variables/constantes, fonctions, interfaces, classes ayant un regroupement logique. Les déclarations dans l’espace de noms vont rester locales à l’espace de noms. Cela limite les conflits possibles de noms car un nom identique pour deux espaces de noms différents ne posera alors plus de problèmes.

Pour rendre accessible une déclaration en dehors de l’espace de noms, on la préfixe par le mot-clé export.

// Espace de nom 
 
  namespace Livres {  
    class Livre {  
        private titre:string;  
        public get Titre() {  
            return this.titre;  
        }  
        constructor(titre:string) {  
            this.titre = titre;  
        }  
    } 
   export class Auteur {  
        nom:string;  
        prenom:string;  
       ...

Conclusion

TypeScript enlève l’excès de flexibilité qui fait la force (simplicité, liberté syntaxique, etc.) mais aussi la faiblesse de JavaScript (évolution, partage du code, etc.). L’ajout d’un typage des données et les possibilités objets transforment JavaScript au même niveau que les langages objets récents. Cependant, il ne semble pas forcément adapté à tous les projets. On peut considérer que TypeScript est davantage adapté à des projets de grande taille ayant des contraintes importantes en termes de développement ou bien à la réalisation de frameworks. D’autre part, le langage JavaScript évolue et nombre d’innovations intégrées à TypeScript comme les classes ont été ajoutées depuis au langage traditionnel rendant l’usage de TypeScript de moins en moins évident.