Introduction à TypeScript
Introduction
Je me forme actuellement sur TypeScript car il est de plus en plus intéressant pour un DevOps, pour ma part ce sont le développement d’actions GitHub et l’utilisation de CDK (AWS Cloud Development Kit) qui me font aller sur ce langage. C’est une introduction, nous allons voir les bases de TypeScript, il y aura tout de même deux prérequis :
- connaitre les bases de JavaScript (même si je vais revenir dessus par moment).
- avoir npm d’installer sur sa station de développement
Allez c’est parti !
Les limites de JavaScript
JavaScript est langage largement utilisé, malgré sa forte populartité on peut lui trouver quelques inconvénients notamment le fait qu’il soit faiblement typé et c’est la raison d’être de TypeScript.
Je vous explique avec ces quelques lignes de code :
a = 5
a = "string"
console.log(a)
> string
Ici l’on peut voir qu’une variable peut changer de type sans une réelle conversion de notre part.
On peut faire appel à des objects qui n’existent pas sans générer aucune erreur :
let user = {
name: "Dimitri"
age: 25
city: "Lyon"
}
console.log(user.name)
console.log(user.zodiac_sign)
> Dimitri
> Undefined
Ici zodiac_sign n’existe pas.
Ce laxisme pouvait être apprécié pendant la phase de développement est très vite contraignant lors d’un debug. TypeScript va simplement ajouter du régidité (positive).
Les avantages de TypeScript
-
Typage statique qui résoud les problèmes vus dans la section précédente.
-
POO (Programmation Orienté Objet)
TypeScript prend en charge la programmation orientée object, les concepts de classe, interface, héritages sont disponibles.(les classes sont utilisables en JS également mais leur fonctionnement diffère des autres langages comme Java par exemple).
- Compatibilité entre JavaScript & TypeScript
Un programme JavaScript est également un programme TypeScript valide, et un programme TypeScript peut consommer de manière transparente du JavaScript.
Installation
Il est temps de mettre les mains à la patte ! Installons le compilateur typescript :
npm i -g typescript
Nous pouvons passer la commande suivante pour s’assurer de sa bonne installation :
tsc -v
Version 4.7.4
Pour bénéficer de la configuration par defaut nous executerons la commande suivante :
tsc --init
Un fichier nommée tsconfig.json doit avoir apparu, il correspond à la configuration de TypeScript comme vous l’aurez compris.
La portée
Un fichier .ts sera soit un module (fermé) soit un script global c’es-à-dire
Modification de la config de base
Pour ne pas être embetté par cette notion sous allons modifier la config par défaut afin de la rendre un peu plus pro :
tsc --initfait apparaitre le fichier de config. Dans ce fichier nous allons modifier trois valeurs :
- target, nous spécifions es6 correspondant à ECMAScript 6 utilisé par tous les navigateurs modernes.
- rootDir, nous indiquons le répertoire racine dans lequel nous glisserons notre fichier
app.ts - outDir, nous écrivons le répertoire de sortie. Lors de la compilation de notre programme avec la commande
tscle compilateur convertir tous les fichiers .ts en fichier .js.
"target": "es6",
[...]
"rootDir": "./src",
[...]
"outDir": "./dist",
Nous executons ensuite la commande tsc --watch qui permet de lancer le compilateur TypeScript, l’option watch permet de recompiler le code à chaque modification.
Maintenant que nous avons fini de configurer notre espace de développement nous allons pouvoir nous amuser avec TypeScript !
Syntaxe
let
Je fais un rapide retour sur le mot clé “let” qui sera utilisé tout au long de cet article, let permet tout simplement de déclarer une variable dont la portée est celle du bloc courant.
Le typage
Comme je vous l’expliquais précédemment, TypeScript va rajouter du typage static, désormais si j’essais de passer une valeur string à a qui est définit comme un number, la console me renvoie une erreur.
let a = 2;
a = "2";
src/app.ts:3:1 - error TS2322: Type 'string' is not assignable to type 'number'.
Dans l’exemple précédent nous avons utilisé l’inférence de types, c’est-à-dire que nous avons laissé l’interpréteur rechercher automatiquement le type associé ; mais nous pouvons spécifier nous même un type comme dans l’exemple ci-dessous.
let b: number;
b = 5
b = "5"
Type 'string' is not assignable to type 'number'.
Dynamic Type
TypeScript apporte un autre concept qui n’est pas présent dans JS : le dynamic type checking. A définir.
Union Type
Nous pouvons également spécifier deux types à une variable grâce à l'union type :
let c: number | string;
c = 4;
c = "4"
Dans cet exemple c prendra comme tyoe number ou string.
Liste des objects globaux
Literal type
Avec le “literal type” nous pouvons de spécifier un ensemble de valeurs (ici string) possibles pour une variable. Dans cet exemple si nous essayons de spécifier une valeur autre que “male” ou “female” nous obtiendrons une erreur.
let sexe: "male" | "female"
Type assertion
De manière générale TypeScript va essayer par lui même de déduire le type de nos différentes variables ou objets. L’assertion de type va nous permettre de définir le type d’une valeur et d’emp¨cher le compitaleur de la déduire.
Au lieu d’inscrire le code suivant :
const btn = document.querySelector(".btn")
Qui permettrait à TypeScript de déduire que btn est un Element ou null.
Nous rajouterons <HTLMButtonElement> qui nous permettra d’indiquer le type souhaité à TypeScript. <HTLMButtonElement> est tout simplement un type.
const btn = <HTLMButtonElement>document.querySelector(".btn")
Il n’est pas évident de connaitre ce genre de type, pour en découvrir davantage pour vous lire la documentation.
Tableau
On revient désormais à des concepts plus classiques ! Dans ce snippet nous déclarons un tableau de type string :
let myArray: string[];
myArray = ["apple", "peach", "banana" ]
Nous pouvons aussi le déclarer de cette manière mais le tableau ne pourra contenir que trois valeurs :
let myArray2 : [string, string, string]
Cette syntaxe est mois répandue.
Tableau deux dimensions
Nous pouvons également créer des tableaux à deux dimensions de cette manière :
let myArray3: number[][];
myArray3 = [[1,3,5,7,9], [0,2,4,6,8]]
Tuples
Comme dans beaucoup d’autres langages nous retrouvons les tuples qui permettent de décrire précisement les différents types de nos valeurs ainsi que leur ordre :
let tuples: [number, string, boolean]
tuples = [1, "toto", false]
Si l’on veut différents types sans se soucier de leur ordre nous pourrons utiliser la syntaxe suivante:
let multiTypeArray: (number | string)[];
multiTypeArray = ["toto", 1, "tata", "apple"]
Boucle & Condition
Dans cette section je vais vous (re)présenter les boucles et les confitions même si nous avons compris que TypeScript n’apporte “que” du typage fort, une boucle entre TS & JS n’aura pas de différence.
Une boucle for avec un index:
for (let i = 0; i < 3; i++) {
console.log ("i =" + i);
}
Une boucle for qui parcourt un tableau :
let fruits = ["apple", "banana", "watermelon"];
for (var fruit of fruits) {
console.log(val); // prints values: apple, banana, watermelon
}
Il existe bien d’autres types de boucles comme while, do…while, … mais je ne vous les présenterai pas aujourd’hui car comme vous pouvez le constater elles sont identiques à celle en JavaScript.
Pour ce qui est des conditions c’est très similire à ce que l’on peut retrouver dans d’autres langages :
let x: number = 10, y = 20;
if (x > y)
{
console.log('x is greater than y.');
}
else if (x < y)
{
console.log('x is less than y.'); //This will be executed
}
else if (x == y)
{
console.log('x is equal to y');
}
Nous allons maintenant aller encore plus loin en abordant les objets !
Object
En JavaScript, le moyen fondamental de regrouper et de transmettre des données est de recourir à des objets. En TypeScript, nous les représentons par des types d’objets.
Si je créé un objet de cette manière a, b & c auront any comme type :
let myObject: {a, b, c};
Any correspond à n’importe quel type. C’est une façon utile et simple pour prototyper quelques chose ou pour retrouver la souplesse du JS quelques instants mais cela reste à éviter.
A ne pas confondre avec unkown qui est similaire à any mais plus restrictif.
Pour spécifier un type j’utiliserai la syntaxe suivante :
let myObject: {a: number, b: any, c: string};
myObject = {
a: 2
b: "2"
c: "2"
}
Interface
Nous allons désormais étudier les interfaces qui nous seront très utiles comme dans beaucoup d’autres langages.
interface Fruit {
name: string;
season: string
}
Il y a peu d'explication à donner c'est assez simple à comprendre, ici je créé une interface nommé __Fruit__ ayant deux paramètres de type string.
Je peux ensuite appeler cette interface de cette manière :
let watermelon: Fruit = {
name: "Watermelon";
season: "Summer"
}
On peut donner plus de précision en spécifiant des propriétés :
interface Fruit {
readonly name: string;
readonly season: string;
type?: string;
quantity: number
}
Dans cet exemple j’ajoute la propriété readonly qui empêchera le changement de la valeur name & season. Le caractère ? quant à lui indique que type est optionnel. Enfin la valeur de quantity pourra changer.
On peut rendre nos interface encore plus intéressante en étendant, c’est-à-dire créer une interface à partir d’une autre inferface, voyons cela.
interface DamagedFruit extends Fruit {
free: true;
}
let banana: DamagedFruit = {
name: "banana"
season: "summer"
quantity: 10,
free: true
}
Dans ce snippet je créé l’interface DamagedFruit qui est une extension de Fruit, ainsi je rajoute la propriété free à l’interface Fruit déjà existante.
Type Aliases
Les alias ou type aliases permettent de définir et/ou renommer des types natifs en TypeScript.
type myBoolean = true | false;
type stringOrNumber = string | number;
let x: stringOrNumber;
x = "4";
x = 4
Ici dans ces quelques lignes d’exemples, je créé :
- le type myBoolean qui peut prendre comme valeur true ou false
- le type stringOrNumber qui peut prendre comme prendre comme valeur une chaine de caractère ou un nombre.
Générique
Ce concept fraichement arriver en Golang existe déjà depuis un moment chez TypeScript, ce concept est utilisé quand nous ne savons pas encore quel type sera nécessaire :
interface Box<Type> {
a: Type
}
let myBox: Box<number> = {
a: 5
}
On peut s’en servir partout, avec les variables, les functions, les objets, les interfaces…
Function
De manière très classique on peut créer des fonctions :
func add(a: number,b: number): number{
return a +b;
}
add(1,2)
Dans ce snippet nous crééons un function add qui prend deux paramètres a & b et retourne un nombre.
Comme dans d’autres langages on peut créer une fonction qui ne retourne aucune valeur grâce au mot clé void :
func add(a: number,b: number): void{
console.log(a +b);
}
Enfin nous pouvons créer une fonction utilisant les génériques comme vu précédemment :
function echo<T>(x: T): T {
return x
}
Il est temps de passer au dernier concept que nous étudirons dans cette introduction : les classes !
Class
Qui dit fonction dit classe (même si tous les langages ne disposent pas de classes).
Nous créons une classe Fruit qui contient 3 variables :
- name
- season
- quantity
class Fruit {
name: string;
season: string;
quantity: number;
Ensuite nous déclarons un constructeur via le mot clé constructor, pour rappel un construteur est une fonction “spéciale” qui est responsable de l’initialisation des variables de la classe.
[...]
constructor(name: string, season: string, quantity: number ) {
this.name = name;
this.season = season;
this.quantity = quantity;
}
Enfin nous invoquons un objet nommé newFruit :
const newFruit = new Fruit("Watermelon", "Summer", 10);
Voici le code complet :
class Fruit {
name: string;
season: string;
quantity: number;
constructor(name: string, season: string, quantity: number ) {
this.name = name;
this.season = season;
this.quantity = quantity;
}
}
const newFruit = new Fruit("Watermelon", "Summer", 10);
Mais nous pouvons être encore plus effiface en réalisant un code plus simple qui fonctionnera de la même manière :
class Fruit {
constructor(private name: string, private season: string, private quantity: number) {}
}
const newFruit = new Fruit("Watermelon", "Summer", 10);
Il suffit d’ajouter le mot clé private devant chacune de nos variables comme vous pouvez le voir. private permet de spécifier que les variables ne sont accessibles uniquement dans la class c’est une syntaxe que nous retrouverons souvent.
Rien de très compliqué en soit.
Conclusion
J’espère que cette introduction à TypeScript t’as plu ! Il y a encore bien des choses à découvrir dans ce langage mais nous en connaissons bien assez pour se lancer dans la programmation d’une action Github, cela sera le prochain article que je vais partager. A la semaine prochaine !