TP 1 - Structures de données

Tableaux

Un tableau (array) est une collection homogène qui stocke des valeurs avec le même type de données

  • ils sont statiques: une fois initialisés, leur dimension ne peut pas être modifiée
  • chaque élément est identifié par un entier unique appelé index
  • ils doivent être déclarés avant l'utilisation
  • l'initialisation fait référence au remplissage du tableau avec des éléments
       let values:string[]; 
       values = ["1","2","3","4"] 
       console.log(values[0]);  //1
       console.log(values[1]);  //2
 
  • il peut être créé aussi à l'aide de l'objet Array, en donnant comme paramètre la taille ou une liste de valeurs. Comme peut-être vous le savez déjà d'autres langages de programmation, les listes peuvent être declarés aussi en utilisant des types de tableau générique (template, Array<T>)
       let arr_names:number[] = new Array<number>(4)  
 
       for(let i = 0;i<arr_names.length;i++) { 
          arr_names[i] = i * 2 
          console.log(arr_names[i]) 
       }
 
       var names:string[] = new Array<string>("Mary","Tom","Jack","Jill") 
 
       for(var i = 0;i<names.length;i++) { 
            console.log(names[i]) 
       }
 

Fonctions prédéfinies pour les tableaux

String

L'objet global String est un constructeur de chaînes de caractères. Les littéraux de chaînes de caractères peuvent avoir l'une des formes suivantes :

"text"
'text'
"'text' in text"
'It\'s a sunny day!' //character escaping

Fonctions prédéfinies pour les chaînes de caractères

  • str.length(EN, FR) - la longueur d'une chaîne
  • str.substring(start, end)(EN, FR) - retourne le sous-string à partir de la position start jusqu'à la position end (le dernier paramètre est optionnel)
  • str.split(separator, limit)(EN, FR) - divise le string dans un array avec des éléments séparés par le separator
    • separator - un string avec les caractères après lesquelles on veut séparer le string original
    • limit - le nombre maximum de divisions (optionnel)
  • str.toUpperCase()(EN, FR) - retourne un nouveau string avec toutes les lettres majuscules
  • str.toLowerCase()(EN, FR) - retourne un nouveau string avec toutes les lettres minuscules
  • str.indexOf(search, index)(EN, FR) - retourne l'index de search
    • search - l'élément cherché
    • index - l'index à partir duquel on commence à chercher
  • str.lastIndexOf(search, index)(EN, FR) - la dernière apparition de search
  • str.trim()(EN, FR) - on supprime tous les espaces du début et de la fin du string
  • str.startsWith(search, index)(EN, FR) - détermine si une chaîne commence par les caractères de search
  • str.endsWith(search, index)(EN, FR) - détermine si une chaîne se termine par les caractères de search
  • str.replace(subStr, newSubStr)(EN, FR) - retourne un nouveau string avec newSubStr au lieu de subStr
  • str.charAt(index)(EN, FR) - retourne le caractère qui se trouve a la position index

Object

Les objets representent la manière fondamentale dont on peut regrouper et transmettre les données. En TypeScript, nous les représentons via des types d'objets.

  • Anonymes
     function imprimerForme(forme: { name: string; surface: number }) {
      return "Surface rectangle: " + forme.surface;
    }
  • Nommés à l'aide d'une interface
     interface Forme{
      name: string;
      surface: number;
    }
     
    function imprimerForme(forme: Forme) {
      return "Surface rectangle: " + forme.surface;
    }
  • Nommés à l'aide d'un alias type
    type Forme = {
      name: string;
      surface: number;
    };
     
    function greet(forme: Forme) {
      return "Surface rectangle: " + forme.surface;
    }

Modificateurs de propriété

Chaque propriété d'un type d'objet peut spécifier plusieurs choses: le type, si la propriété est facultative et si la propriété peut être écrite.

Propriétés Facultatives

La plupart du temps, nous nous retrouverons face à des objets qui pourraient avoir un ensemble de propriétés. Dans ces cas, nous pouvons marquer ces propriétés comme facultatives en ajoutant un point d'interrogation (?) a la fin de leurs noms. Si on a une propriété facultative, cela veut dire qu'au moment de l'utilisation de l'objet, elle peut etre définie ou pas, comme dans l'example suivant:

 interface PaintOptions {
  shape: Shape;     //obligatoire
  xPos?: number;    //facultatif
  yPos?: number;    //facultatif
}
 
function paintShape(opts: PaintOptions) {
  // ...
}
 
const shape = getShape();
paintShape({ shape });   //valide
paintShape({ shape, xPos: 100 });   //valide
paintShape({ shape, yPos: 100 });   //valide
paintShape({ shape, xPos: 100, yPos: 100 });   //valide

La déclaration nom?: type représente en fait une matiere plus efficace de declarer les variables comme nom: type | null | undefined.

Par exemple:

let nom: string = null;   // Incorrect, car la variable doit recevoir un string lors de l'assignation, et null n'est pas un string
 
let nom?: string = null; // Correct, car la construction ?: signifie que nom peut etre soit string, soit null, soit undefined

Attention!. On peut aussi lire depuis les propriétés facultatives, mais TypeScript va réaliser strictNullChecks et il va nous avertir que la variable pourrait etre undefined.

function paintShape(opts: PaintOptions) {
  let xPos: number = opts.xPos;
//    ^ = Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.ts(2322)
  let yPos: number = opts.yPos;
//    ^ = Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.ts(2322)
}

Afin de gérer ce probleme, on peut utiliser l'opérateur ternaire.

function paintShape(opts: PaintOptions) {
  let xPos: number = opts.xPos === undefined ? 0 : opts.xPos;
  let yPos: number = opts.yPos === undefined ? 0 : opts.yPos;
 
  let xPos: number = opts.xPos ?? 0; // Si opts.xPos est null ou undefined, la variable recevra 0; sinon, l'expression évaluera le parametre de gauche
}

Propriétés Readonly

Les propriétés peuvent également être marquées comme readonly pour TypeScript. Bien qu'il ne modifie aucun comportement lors de l'exécution, une propriété marquée comme readonly ne peut pas être écrite pendant la vérification de type.

interface Forme {
  readonly name: string;
}
 
function afficherInfo(obj: Forme) {
  // We can read from 'obj.prop'.
  console.log(`prop has the value '${obj.name}'.`);
 
  // But we can't re-assign it.
  obj.name= "hello";
// ERROR: Cannot assign to 'name' because it is a read-only property.
}

L'utilisation du modificateur readonly n'implique pas nécessairement qu'une valeur est totalement immuable - ou en d'autres termes, que son contenu interne ne peut pas être modifié. Cela signifie simplement que la propriété elle-même ne peut pas être réécrite.

interface Home {
  readonly resident: { name: string; age: number };
}
 
function visitForBirthday(home: Home) {
  // We can read and update properties from 'home.resident'.
  console.log(`Happy birthday ${home.resident.name}!`);
  home.resident.age++;
}
 
function evict(home: Home) {
  // But we can't write to the 'resident' property itself on a 'Home'.
  home.resident = {
// Error: Cannot assign to 'resident' because it is a read-only property.
    name: "ALF student",
    age: 42,
  };
}

Extension des types

Soit l'interface suivante et supposons qu'on veut créer une interface qui contienne les memes propriétés, mais aussi d'autres supplémentaires. Par conséquent, on va utiliser l'extension des types, pour ne pas redéclarer une nouvelle interface presqu'identique:

interface BasicAddress {
  name?: string;
  street: string;
  city: string;
  country: string;
  postalCode: string;
}
 
interface AddressWithUnit extends BasicAddress {
  unit: string;
}

Ou bien, si on veut étendre 2 interfaces, on peut utiliser:

interface Colorful {
  color: string;
}
 
interface Circle {
  radius: number;
}
 
interface ColorfulCircle extends Colorful, Circle {}
 
const cc: ColorfulCircle = {
  color: "red",
  radius: 42,
};

Intersection des types

TypeScript fournit une autre construction appelée type intersection qui est principalement utilisée pour combiner des types d'objets existants.

Un type d'intersection est défini à l'aide de l'opérateur &.

interface Colorful {
  color: string;
}
interface Circle {
  radius: number;
}
 
type ColorfulCircle = Colorful & Circle;
function draw(circle: Colorful & Circle) {
  console.log(`Color was ${circle.color}`);
  console.log(`Radius was ${circle.radius}`);
}
 
// okay
draw({ color: "blue", radius: 42 });

Réunion des types

La réunion des types décrit une valeur qui peut recevoir l'un de plusieurs types. Nous utilisons la barre verticale | pour séparer chaque type, donc number| string| boolean est le type d'une valeur qui peut être un number, une string ou un boolean.

interface Bird {
  fly(): void;
  layEggs(): void;
}
 
interface Fish {
  swim(): void;
  layEggs(): void;
}
 
declare function getSmallPet(): Fish | Bird;
 
let pet = getSmallPet();
pet.layEggs();
 
// Only available in one of the two possible types
pet.swim();
Property 'swim' does not exist on type 'Bird | Fish'.
  Property 'swim' does not exist on type 'Bird'.

Classes

Dans la programmation orientée objet, une classe est un modèle de code de programme extensible pour créer des objets, fournissant des valeurs initiales pour l'état (variables) et des implémentations de comportement (fonctions ou méthodes). En JavaScript, on peut créer des applications en utilisant cette approche basée sur les classes orientée objet. Dans TypeScript, on permet aux développeurs d'utiliser ces techniques maintenant et de les compiler en JavaScript qui fonctionne sur tous les principaux navigateurs et plates-formes, sans avoir à attendre la prochaine version de JavaScript.

class Animal {
  name: string;
 
  constructor(message: string) {
    this.name= message;
  }
 
  printBreed() {
    return "This animal is a " + this.name;
  }
}
 
let bear = new Greeter("bear");

Héritage

Dans TypeScript, nous pouvons utiliser des modèles orientés objet courants. L'un des modèles les plus fondamentaux de la programmation basée sur les classes est de pouvoir étendre les classes existantes pour en créer de nouvelles à l'aide de l'héritage.

class Animal {
  name: string;
  constructor(theName: string) {
    this.name = theName;
  }
  move(distanceInMeters: number = 0) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}
 
class Snake extends Animal {
  constructor(name: string) {
    super(name);
  }
  move(distanceInMeters = 5) {
    console.log("Slithering...");
    super.move(distanceInMeters);
  }
}
 
class Horse extends Animal {
  constructor(name: string) {
    super(name);
  }
  move(distanceInMeters = 45) {
    console.log("Galloping...");
    super.move(distanceInMeters);
  }
}
 
let sam = new Snake("Sammy the Python");
let tom: Animal = new Horse("Tommy the Palomino");
 
sam.move();
tom.move(34);

Chaque classe dérivée qui contient une fonction constructor doit appeler super() qui exécutera le constructeur de la classe de base. De plus, avant d’accéder à une propriété à ce sujet dans un corps de constructeur, nous devons appeler super(). C'est une règle importante que TypeScript appliquera.

L'exemple montre également comment remplacer les méthodes de la classe de base par des méthodes spécialisées pour la sous-classe. Ici, Snake et Horse créent une méthode de déplacement qui remplace le déplacement d'Animal, lui donnant des fonctionnalités spécifiques à chaque classe. Notez que même si tom est déclaré comme un animal, puisque sa valeur est un cheval, appeler tom.move (34) appellera la méthode de substitution dans Horse.

Modificateurs

Par défaut, tous les membres d'une classe dans TypeScript sont de type public. Tous les membres publics sont accessibles n'importe où sans aucune restriction. Le modificateur d'accès private garantit que les membres de la classe ne sont visibles que pour cette classe et ne sont pas accessibles en dehors de la classe ou ils ont été créés. Le modificateur d'accès protected est similaire au modificateur d'accès privé, sauf que les membres protégés sont accessibles à l'aide de leurs classes dérivantes.

class Employee {
    public empName: string;
    private empEmail: string;
    protected empCode: number;
 
    constructor(name: string, email: string, code: number){
        this.empName = name;
        this.empEmail = email;
        this.empCode = code;
    }
}
 
class SalesEmployee extends Employee{
    private department: string;
 
    constructor(name: string, email: string, code: number, department: string) {
        super(name, email, code);
        this.department = department;
    }
    printProperties() {
        console.log(this.empName + " " + this.empEmail + " " + this.empCode);
                                     //   ^ Property 'empEmail' is private and only accessible within class 'Employee'.ts(2341)
    }
}
 
let empObj = new SalesEmployee("John Smith", "john@gmail.com", 123, "Sales");
console.log(empObj);
empObj.empEmail; // Property 'empEmail' is private and only accessible within class 'Employee'.ts(2341)
empObj.empCode; // Property 'empCode' is protected and only accessible within class 'Employee' and its subclasses.ts(2445)

Une autre forme de créer le constructeur est à l'aide des paramètres de la classe:

constructor (public n: number, private s: number) {}
 
/* identique à */
 
constructor (n: number, s: number) {
     this. n = n;
     this.s = s;
}

Accesseurs

TypeScript prend en charge les getters / setters comme moyen d'intercepter les accès à un membre d'un objet. Cela vous donne un moyen d'avoir un contrôle plus fin sur la façon dont un membre est accédé sur chaque objet.

class Employee {
  private _fullName: string = "";
 
  get fullName(): string {
    return this._fullName;
  }
 
  set fullName(newName: string) {
    this._fullName = newName;
  }
}
 
let employee = new Employee();
employee.fullName = "Bob Smith";
 
if (employee.fullName) {
  console.log(employee.fullName);
}

Propriétés statiques

Jusqu'à présent, nous n'avons parlé que des membres d'instance de la classe, ceux qui apparaissent sur l'objet lorsqu'il est instancié. Nous pouvons également créer des membres statiques d'une classe, ceux qui sont visibles sur la classe elle-même plutôt que sur les instances. Dans cet exemple, nous utilisons static sur origin, car il s'agit d'une valeur générale pour toutes les grilles. Chaque instance accède à cette valeur en ajoutant le nom de la classe. De même pour ajouter this. devant les accès aux instances, nous ajoutons ici Grid. devant les accès statiques.

class Grid {
    static origin = { x: 0, y: 0 };
 
    calculateDistanceFromOrigin(point: { x: number; y: number }) {
      let xDist = point.x - Grid.origin.x;
      let yDist = point.y - Grid.origin.y;
      return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
    }
 
    constructor(public scale: number) {}
  }
 
  let grid1 = new Grid(1.0); // 1x scale
  let grid2 = new Grid(5.0); // 5x scale
 
  console.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 }));
  console.log(grid2.calculateDistanceFromOrigin({ x: 10, y: 10 }));

TypeScript Class vs. Interface

Les classes et les interfaces sont des structures puissantes qui facilitent non seulement la programmation orientée objet, mais également la vérification de type dans TypeScript. Une classe est un plan à partir duquel nous pouvons créer des objets qui partagent la même configuration - propriétés et méthodes. Une interface est un groupe de propriétés et de méthodes associées qui décrivent un objet, mais ne fournit ni implémentation ni initialisation pour eux.

Fonctions

Les fonctions sont sont fondamentales toute application en JavaScript. Ils vous permettent de créer des couches d’abstraction, d’imiter des classes, de masquer des informations et de modules. Dans TypeScript, bien qu'il existe des classes, des espaces de noms et des modules, les fonctions jouent toujours le rôle clé dans la description de la façon de faire les choses.

  • Fonctions nommées
    function add(x: number, y: number): number {
      return x + y;
    }
  • Fonctions anonymes
    let myAdd: (x: number, y: number) => number = function (
      x: number,
      y: number
    ): number {
      return x + y;
    }; 

Arguments de la ligne de commande

La propriété process.argv renvoie un tableau contenant les arguments de la ligne de commande transmis lors du lancement du processus Node.js. Le premier élément sera process.execPath (le chemin d'executable node). Le deuxième élément sera le chemin vers le fichier JavaScript en cours d'exécution. Les autres éléments seront des arguments de ligne de commande supplémentaires.

Lancement du processus Node.js pour le fichier process.js :

$ node process.js one 2 three
//process.js
 
for(let argumentIndex:number = 0; argumentIndex < process.argv.length; argumentIndex++) {
    console.log(argumentIndex + ": " + process.argv[argumentIndex]);
}
 
/* Output:
0: C:\Program Files\nodejs\node.exe
1: C:\Users\student\Desktop\process.js
2: one
3: 2
4: three
*/

Fonctions parseInt et parseFloat

parseInt

La fonction parseInt() analyse une chaîne de caractère fournie en argument et renvoie un entier exprimé dans une base donnée.

let string_var: string = "12";
let base: number = 10;
let number_value: number = parseInt(string_var, base);
 
console.log(number_value); //12
  • string_var - La valeur qu'on souhaite analyser et convertir. Si l'argument string n'est pas une chaîne de caractères, elle sera convertie en une chaîne. Les blancs contenus au début de l'argument sont ignorés.
  • base - Un entier compris entre 2 et 36 qui représente la base utilisée pour la valeur représentée dans la chaîne. La base utilisée en général est la base décimale (valeur 10 pour ce paramètre).

parseFloat

La fonction parseFloat() permet de transformer une chaîne de caractères en un nombre réel.

let string_var: string = "15.35";
let number_value: number = parseFloat(string_var);
 
console.log(number_value); //15.35
  • string - Une chaîne de caractères qui contient la valeur qu'on souhaite analyser et transformer dans un nombre flottant.

Exercices

  1. Créez un programme et exécutez-le avec des paramètres dans la ligne de commande. Sauvegardez la liste des parametres dans une variable. Affichez : (1p)
    1. la liste des paramètres avec console.log
    2. le nombre de paramètres
    3. les paramètres avec un for(à votre choix)
  2. Faites un programme qui contient une fonction power qui reçoit deux nombres comme paramètres, qui calcule la puissance et qui : (1p)
    1. affiche le résultat
    2. retourne le résultat
    3. stocke le résultat dans une variable et l'affiche
  3. A l'aide des interfaces, créez un nouveau type de données nommé Employé, qui contienne les champs suivants: nom, prénom, département, expérience (numéro) (2p)
    1. Créez un objet de type Employée qui recoit ses paramètres de la ligne de commande (node index.js NomEmp PrenomEmp ALF 3)
    2. Affichez chaque attribut de l'objet
    3. Faites un array avec 3 objets de type Employee
    4. Affichez l'étudiant avec la plus grande expérience
    5. Affichez la moyenne géométrique des années d'expérience de tous les employés
    6. Affichez la personne avec le prénom le plus longue
    7. Affichez tous les objets en ordre alphabétique apres le nom de famille
  4. Créez une classe Employé ayant les memes propriétés définies pour l'interface de l'exercice précedent (1.5p)
    1. Créez une sous-classe Manager, dérivée de la classe Employé, qui contient un champ supplémentaire noSubordonnés (number)
    2. Créez un objet de type Manager
    3. Créez une fonction qui recoit comme paramètre un string (nom d'une propriété) et qui vérifie si l'objet de type Manager a la propriété indiquée
  5. Écrivez un programme qui retourne un tableaux avec les sous-strings d'un string donnée. (ex: ['c', 'co', 'cod', 'o', 'od', 'd'] pour le string cod) (1p)
  6. Soit un objet de type Manager. Ecrivez un programme pour le convertir dans une liste de paires [key, value]. Par example: (1.5p)
    let newManager = new Manager("LastName", "FirstName", "Departement", 10, 30);
    let keyValueList: (string|number)[][];
    /* Here goes your code */
    console.log(keyValueList); // [["nom", "LastName"], ["prenom", "FirstName"], ["department", "Departement"], ["experience", 10], ["noSubordonnes", 30]]
  7. Soit le code suivant. Définissez le type Person et apportez les modifications nécessaires de sorte que le programme affiche la liste des objets de type Person (1.5p)
    interface User {
        name: string;
        age: number;
        occupation: string;
    }
     
    interface Admin {
        name: string;
        age: number;
        role: string;
    }
     
    export type Person = unknown; /* TODO: Definissez ici le type */
     
    export const persons: User[] /* TODO: Modifiez en Person[] */ = [
        {
            name: 'John Doe',
            age: 25,
            occupation: 'Chimney sweep'
        },
        {
            name: 'Jane Doe',
            age: 32,
            role: 'Administrator'
        }
    ];
     
    export function logPerson(user: User) {   /* TODO: Modifiez pour afficher la liste des objets Person */
        console.log(` - ${user.name}, ${user.age}`);
    }
     
    persons.forEach(logPerson);
  8. On dit qu'une phrase est pangram si elle utilise toutes les lettres d'un alphabet donné. En utilisant la classe ci-dessous, définissez les éléments manquants afin de vérifier si une phrase choisie par vous est pangram. (1.5p)
    class Pangram {
        private alphabet: string = 'abcdefghijklmnopqrstuvwxyz';
     
        constructor(private phrase: string) {
          /* TODO */
        }
     
        isPangram(): boolean {
          /* TODO */
          return true;
        }
    }
alf/laboratoare/01.txt · Last modified: 2022/03/06 16:20 by diana.ghindaoanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0