Apparence
Théorie - Semaine 8 - TypeScript et introduction au Cadriciel Front-end
TypeScript, où encore un autre langage de programmation?
Pourquoi TypeScript?
TypeScript est un langage de programmation libre et open source développé par Microsoft qui a pour but d'améliorer et de sécuriser la production de code JavaScript. Il s'agit d'un surensemble syntaxique strict de JavaScript (c'est-à-dire que tout code JavaScript correct peut être utilisé avec TypeScript). Le code TypeScript est transcompilé en JavaScript, et peut ainsi être interprété par n'importe quel navigateur web ou moteur JavaScript. TypeScript a été co-créé par Anders Hejlsberg, principal inventeur de C#.
TypeScript permet un typage statique optionnel des variables et des fonctions, la création de classes et d'interfaces, l'import de modules, tout en conservant l'approche non-contraignante de JavaScript. Il supporte la spécification ECMAScript 6.
Source : Wikipédia
Truc 💡
En soi, TypeScript n'est pas un langage de programmation à part entière, mais plutôt une extension de JavaScript qui ajoute des fonctionnalités de typage statique et d'autres fonctionnalités avancées. Cependant, il est souvent considéré comme un langage de programmation à part entière en raison de sa popularité et de son utilisation généralisée dans le développement web.
Tout ce qu'on a fait depuis le début du cours s'applique aussi puisque TypeScript est un sur-ensemble de JavaScript.
Avantage notable de TypeScript
- Typage statique : TypeScript permet de définir des types pour les variables, les fonctions, les classes, etc. Cela permet de détecter les erreurs de type à la compilation.
- Autocomplétion dans les éditeurs de code : Grâce au typage statique, les éditeurs de code peuvent fournir une autocomplétion plus précise et des suggestions de code.
- Donne les avantages de la programmation orientée objet : TypeScript supporte les classes, les interfaces, l'héritage, etc., ce qui facilite la structuration du code et la réutilisation.
Attention ⚠
Cependant, comme le typage est optionnel, pour avoir tous les bénéfices, c'est important de bien intégrer les types à l'intérieur du même module.
Truc 💡
Par expérience, vous devriez toujours installer TypeScript au début du projet. Sinon, ajouter TypeScript à un projet déjà commencé peut être plus compliqué, surtout si le code existant n'est pas structuré de manière à faciliter l'ajout de types. En installant TypeScript dès le début, vous pouvez bénéficier de ses avantages tout au long du développement et éviter les problèmes potentiels liés à l'ajout de types à un code déjà écrit.
Tuto/Exercice - Première application TypeScript
Premièrement, il faut installer TypeScript de manière globale sur votre ordinateur :
bash
npm install -g typescriptInformation ❗
Rappel! -g signifie que le paquet est installé globalement, ce qui permet d'utiliser la commande tsc (le compilateur TypeScript) dans n'importe quel projet sur votre machine.
Il est important de toujours vérifier que TypeScript est bien installé en utilisant la commande suivante :
bash
tsc --versionEnsuite, dans un dossier de travail de votre choix, créer votre premier fichier TypeScript : main.ts avec le contenu suivant :
typescript
console.log('Hello World!');Attention ⚠
Nouvelle extension de fichier : .ts pour les fichiers TypeScript, au lieu de .js pour les fichiers JavaScript.
Information ❗
On se rappelle que TypeScript est un sur-ensemble de JavaScript, donc tout code JavaScript valide est aussi du TypeScript valide. Par conséquent, le code ci-dessus est à la fois du JavaScript et du TypeScript.
Maintenant, pour compiler le fichier TypeScript en JavaScript, utilisez la commande suivante dans votre terminal :
bash
tsc main.tsAttention ⚠
Il se peut que vous ayez une erreur Cannot find name 'Map'.... Ignorer la pour l'instant, cela va être corrigé lorsque nous allons utiliser vue.
Puis pour exécuter le fichier JavaScript généré, utilisez la commande suivante :
bash
node main.jsIl est également possible de rediriger la sortie de la compilation vers un dossier spécifique en utilisant l'option --outDir :
bash
tsc main.ts --outDir js
node ./js/main.js-- outdir va créer le dossier spécifier (au besoin) et y mettre le fichier compilé.
Particularité de la syntaxe TypeScript
Déclarer une variable
Petit rappel de déclaration de variable en JavaScript :
javascript
let message = 'Hello World!';
const pi = 3.14;Il est a notée que tout code JavaScript valide est aussi du TypeScript valide. Par contre, il se peut que l'éditeur vous donne des erreurs de type, car TypeScript essaie de deviner le type de la variable à partir de sa valeur initiale.
Truc 💡
Toujours utiliser const et let seulement si la variable est modifier plus tard
Les types
TypeScript utilise les mêmes types que JavaScript tout en rajoutant le type any qui permet de désactiver le typage statique pour une variable spécifique. Voici quelques exemples de types en TypeScript :
typescript
let a: number;
let b: boolean;
let c: string;
let d: any;
let e: number[] = [1, 2, 3];
let f: any[] = [1, true, 'a', false];
let g: object = {};Par défaut, le type d'une variable est any si elle n'est pas initialisée avec une valeur. Par exemple :
typescript
let a;
a = 1;
a = true;
a = 'a'; // aucune erreurAttention ⚠
Si on instancie une variable lors de la déclaration sans spécifier le type, le type sera lui de la valeur. (comme en JavaScript)
typescript
let nombre = 5;
nombre = 'a'; // erreurAssertions de type
L'assertion de type permet de dire à TypeScript de traiter une variable comme un type spécifique, même si le type réel de la variable est différent.
C'est l'équivalent de dire à son compilateur : "J'en sais plus sur la valeur de cette variable que toi, fait confiance à mon jugement".
Voici les syntaxes d'assertion de type en TypeScript :
typescript
let uneValeur;
uneValeur = 'allo';
let strLength: number = uneValeur.length; // pas d'autocomplétiontypescript
let uneValeur;
uneValeur = 'allo';
let strLength: number = (uneValeur as string).length; // autocomplétiontypescript
let uneValeur;
uneValeur = 'allo';
let strLength: number = (<string>uneValeur).length; // autocomplétion, même chose, question de préférenceTruc 💡
Surtout utile lorsque l'on manipule des éléments du DOM.
Fonction lambda
Il s'agit d'une fonction anonyme, c'est-à-dire une fonction qui n'a pas de nom. En JavaScript, on peut déclarer une fonction lambda de la manière suivante :
javascript
const add = function(a, b) {
return a + b;
};Maintenant avec TypeScript, on peut déclarer une fonction lambda de la manière suivante :
typescript
const add = (a: number, b: number): number => {
return a + b;
};S’il n’y a aucun paramètre, on peut faire :
typescript
let afficheAllo = () => {
console.log('allo');
}Interfaces vs Classes
Une interface spécifie un contrat des attributs à respecter et même des fonctions (seulement la signature des fonctions).
Une classe décrie un objet que vous pouvez instancier avec ses attributs, ses méthodes et son constructeur.
Interface
Voici un exemple d'interface en TypeScript :
typescript
interface Personnage {
nom: string;
}
function afficherNom(personnage: Personnage) {
console.log(personnage.nom);
}
let unPersonnage = { nom: "Bob", classe: "fighter" };
afficherNom(unPersonnage);Dans cet exemple, l'interface Personnage spécifie que tout objet de type Personnage doit avoir une propriété nom de type string. La fonction afficherNom prend un paramètre de type Personnage et affiche son nom. L'objet unPersonnage a une propriété nom, donc il est compatible avec l'interface Personnage, même s'il a une propriété supplémentaire classe.
Attention ⚠
Une interface n'est pas comme une interface utilisateur (GUI)
Une interface n'est pas tout à fait comme les interfaces en Java.
Classe
Voici un exemple de classe en TypeScript :
typescript
class Point {
x: number;
y: number;
dessiner(): void {
console.log(`x: ${this.x}, y: ${this.y}`);
}
obtenirDistance(p: Point): number {
return Math.sqrt(
(p.x - this.x) * (p.x - this.x) + (p.y - this.y) * (p.y - this.y)
);
}
}
let p = new Point();
p.x = 2;
p.y = 3;
p.dessiner();
console.log(p.obtenirDistance({ x: 3, y: 4 } as Point));Truc 💡
Classe = définition.
Objet = instance de la classe.
Constructeurs
Pour une classe, il ne peut y avoir qu'un seul constructeur. On va devoir utiliser le mot clé constructor pour définir le constructeur de la classe. Voici un exemple de classe avec un constructeur en TypeScript :
typescript
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
dessiner(): void {
console.log(`x: ${this.x}, y: ${this.y}`);
}
}
let p = new Point(2, 3);
p.dessiner();Il est également possible de rendre des paramètres optionnels en utilisant le symbole ? après le nom du paramètre dans le constructeur. Par exemple :
typescript
class Point {
x: number;
y: number;
constructor(x?: number, y?: number) {
this.x = x ? x : 0;
this.y = y ?? 0;
}
dessiner(): void {
console.log(`x: ${this.x}, y: ${this.y}`);
}
}
let p = new Point(2, 3);
p.dessiner();Attention ⚠
Qu'est-ce que le ?? ? C'est l'opérateur de coalescence nulle, qui retourne la valeur de droite si la valeur de gauche est null ou undefined, sinon il retourne la valeur de gauche. C'est une manière plus concise d'écrire this.y = y ? y : 0;.
Modificateurs d'accès
TypeScript supporte les modificateurs d'accès public, private et protected pour les membres d'une classe.
public: les membres sont accessibles de n'importe où (par défaut).private: les membres sont accessibles uniquement à l'intérieur de la classe.protected: les membres sont accessibles à l'intérieur de la classe et dans les classes qui en héritent.
Exemple avec private :
typescript
class Point {
private x: number;
private y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
dessiner(): void {
console.log(`x: ${this.x}, y: ${this.y}`);
}
}
let p = new Point(2, 3);
// p.x et p.y non-accessible
p.dessiner();Avec TypeScript, il est également possible de déclarer et d'initialiser les propriétés d'une classe directement dans le constructeur en utilisant des modificateurs d'accès. Le code suivant est l'équivalent du code précédent.
typescript
class Point {
constructor(private x: number, private y: number) {
}
dessiner(): void {
console.log(`x: ${this.x}, y: ${this.y}`);
}
}
let p = new Point(2, 3);
// p.x et p.y non-accessible
p.dessiner();Accès aux propriétés
Il est possible d'utiliser des getters et des setters pour accéder et modifier les propriétés d'une classe. Voici un exemple de classe avec des getters et des setters en TypeScript :
typescript
class Point {
constructor(private _x: number, private _y: number) {
}
dessiner(): void {
console.log(`x: ${this._x}, y: ${this._y}`);
}
get x(): number{
return this._x;
}
set x(valeur: number) {
if (valeur < 0)
throw new Error('La valeur ne peut pas être plus petite que 0.');
this._x = valeur;
}
get y(): number{
return this._y;
}
set y(valeur: number) {
if (valeur < 0)
throw new Error('La valeur ne peut pas être plus petite que 0.');
this._y = valeur;
}
}
let p = new Point(2, 3);
p.x = 3;
p.dessiner();TypeScript nous fournit les instructions get et set pour créer des getters et des setters de manière plus concise.
Truc 💡
Il est important de bien définir l'accessibilité des propriétés d'une classe. Cela permet de restreindre l'accès à certaines propriétés et de protéger l'intégrité des données de la classe.
Par conséquent, si on utilise le modificateur d'accès public, on n’aura pas besoins de getters et setters, car les propriétés seront accessibles directement.
Attention ⚠
Notez que l'attribut privé a maintenant un _ devant le nom. Cela nous permet de ne pas avoir le même nom pour la propriété et pour l'attribut. En JavaScript, on doit toujours utiliser le camelCase. Mettre le _ seulement si l'attribut est lié à une propriété.
Utilisation d'une interface dans une classe
Il est possible d'implémenter une interface dans une classe en utilisant le mot-clé implements. Voici un exemple avec la classe Point qui implémente l'interface Coordonnee en TypeScript :
typescript
interface Coordonnee {
x: number;
y: number;
dessiner(): void;
}
class Point implements Coordonnee {
constructor(public x: number, public y: number) {
}
dessiner(): void {
console.log(`x: ${this.x}, y: ${this.y}`);
}
}
let p = new Point(2, 3);
p.x = 3;
p.dessiner();Dans cet exemple, l'interface Coordonnee spécifie que tout objet de type Coordonnee doit avoir des propriétés x et y de type number, ainsi qu'une méthode dessiner(). La classe Point implémente l'interface Coordonnee, ce qui signifie qu'elle doit fournir une implémentation pour toutes les propriétés et méthodes définies dans l'interface. Si on enlève la méthode dessiner() de la classe Point, TypeScript nous donnera une erreur, car la classe ne respecte plus le contrat de l'interface Coordonnee.
On peut aussi gérer le polymorphisme sans être obligé de vérifier si la classe implémente bien ce que l'on veut. Par exemple, si on définit une classe Cadrant qui utilise n'importe quelle Coordonnee.
typescript
class Cadrant {
constructor(private points: Array<Coordonnee>) {
}
dessiner(): void {
console.log(`Voici la liste des points du cadrant :`);
this.points.forEach(element => {
element.dessiner();
});
}
}À l'intérieur de la fonction dessiner, on est sûr d'avoir une liste de Coordonnee qui possède la fonction dessiner.
Modules
Lorsque l'on travaille avec des langages comme le JS ou le TS, il est important de structurer notre code en utilisant des modules.
RAPPEL! Un module est un fichier qui contient du code qui peut être importé et utilisé dans d'autres fichiers. En TypeScript, on peut utiliser les mots-clés export et import pour créer et utiliser des modules. Voici un exemple de module en TypeScript :
typescript
export class Point {
constructor(private x: number, private y: number) {
}
dessiner(): void {
console.log(`x: ${this.x}, y: ${this.y}`);
}
}typescript
import { Point } from './point';
let p = new Point(2, 3);
p.dessiner();Truc 💡
Notez l'utilisation du chemin relatif, sans le .ts. Il est possible de mettre plusieurs éléments entre les accolades, s'il y a plusieurs éléments qui sont exportés.
Lorsque nous utilisons les bibliothèques de Vue, il faut écrire le nom de la bibliothèque au lieu du chemin relatif vers le fichier .ts.
Il est à noter que :
- Un fichier
.tsest considéré comme un module si celui-ci exporte quelque chose. - On doit compiler le fichier
main.tspour que le code depoint.tssoit accessible, car le code depoint.tsest transcompilé en JavaScript et importé dansmain.js.
À vous de jouer!
À l'aide du principe de module et de classe, créer une petite calculatrice simple pouvant faire une addition et une soustraction. Assurez-vous de garder l'historique de vos calculs.
Vous devriez avoir 2 fichiers TypeScript.
C'est partie!
Solution ✅
typescript
export class Calculatrice {
private historiques: string[] = [];
constructor() {
}
additionner(a: number, b:number){
let calcul:number = a + b;
this.ajouterHistorique(`${a} + ${b} = ${calcul}`);
return calcul;
}
soustraire(a:number, b:number){
let calcul:number = a - b;
this.ajouterHistorique(`${a} - ${b} = ${calcul}`);
return calcul;
}
afficherHistorique(){
for(let calcul of this.historiques){
console.log(calcul);
}
}
ajouterHistorique(calcul: string){
this.historiques.push(calcul);
}
}typescript
import { Calculatrice } from "./calculatrice";
let calculatrice = new Calculatrice();
calculatrice.additionner(5, 3);
calculatrice.afficherHistorique();