Accueil / Articles / Opérateurs TypeScript en pratique : mathématiques, comparaisons, logique et ternaires

Opérateurs TypeScript en pratique : mathématiques, comparaisons, logique et ternaires

Une présentation pratique des opérateurs arithmétiques, d’affectation, de comparaison, logiques, d’incrémentation et ternaires de TypeScript, y compris la vérification d’égalité que le compilateur refuse.

2635 mots

Lorsque vous connaissez la manière dont TypeScript attribue et applique les types, l’étape suivante consiste à effectuer des opérations sur ces valeurs : les additionner, les comparer, combiner des conditions et choisir entre différentes alternatives. Les opérateurs sont les symboles qui effectuent ces tâches, et la plupart se comportent exactement comme en JavaScript. Cependant, certains cachent des détails qui causent fréquemment des erreurs, tels que l’égalité lâche par rapport à l’égalité stricte et la différence entre x++ et ++x.

Ce guide aborde les opérateurs arithmétiques, d’affectation, relationnels, logiques, d’incrémentation et de décrémentation, ainsi que les opérateurs ternaires, avec de petits exemples que vous pouvez analyser sans avoir à les exécuter. Au fil du texte, vous verrez où le compilateur de TypeScript ajoute des mécanismes de sécurité absents du JavaScript pur.

Un raccourci d’abord : plusieurs déclarations en une seule instruction

Avant d’examiner les opérateurs, il y a une commodité syntaxique sur laquelle reposent les exemples suivants. La manière habituelle de déclarer deux nombres consiste à utiliser une instruction par variable :

let a: number = 10;
let b: number = 20;

Un seul let peut également introduire plusieurs variables en même temps si on les sépare par des virgules :

let a: number = 10, b: number = 20;

Les deux formes se traduisent par le même résultat ; la seconde est simplement plus concise. Chaque variable de la liste possède sa propre annotation, ce qui leur permet d’avoir des types différents :

let a: number = 10, name: string = "John", isActive: boolean = true;

Utilisez cette forme avec modération. Elle est lisible pour un couple de valeurs étroitement liées, comme a et b utilisés tout au long de ce guide, mais une ligne mélangeant un compteur, un nom et un indicateur est plus difficile à parcourir que trois déclarations séparées.

Opérateurs arithmétiques

Il s’agit des symboles familiers de la mathématique scolaire. Les exemples ci-dessous commencent par deux nombres :

// Arithmetic operators
let a: number = 10, b: number = 20;

Chaque ligne applique alors un opérateur et affiche le résultat dans un commentaire :

console.log(a + b);   // 30   → addition
console.log(a - b);   // -10  → subtraction
console.log(a * b);   // 200  → multiplication
console.log(a / b);   // 0.5  → division
console.log(a % b);   // 10   → modulus (remainder after division)
console.log(a ** b);  // 100000000000000000000 → exponentiation (10 to the power 20)

Fonctionnement de chacun d’eux :

  • + additionne les deux opérandes.
  • - soustrait l’opérande de droite de celui de gauche.
  • * les multiplie.
  • / les divise sans arrondi. 10 / 20 vaut réellement 0.5 ; il n’existe pas de division entière distincte comme dans d’autres langages, car tout nombre en JavaScript est une valeur à virgule flottante.
  • % renvoie le reste de la division. 10 % 20 donne 10 : vingt ne rentre pas zéro fois dans dix, donc tout dix reste en surplus.
  • ** élève l’opérande de gauche à la puissance de l’opérande de droite. 10 ** 20 correspond à dix multiplié par lui-même vingt fois, ce qui explique le résultat très long.

Une précaution à prendre avec ** et les résultats de grande taille : les nombres dépassant Number.MAX_SAFE_INTEGER (environ neuf quadrillions) perdent leur précision entière. 10 ** 20 s’affiche effectivement de manière exacte, mais les opérations arithmétiques sur des valeurs de cette taille ne sont pas fiables ; utilisez bigint lorsque vous avez besoin d’entiers de grande taille exacts.

Affectation et affectation composée

Le simple = stocke une valeur dans une variable. JavaScript et TypeScript proposent également des opérateurs d’affectation composée qui effectuent une opération arithmétique et stockent le résultat dans une seule expression. Commencez avec deux valeurs :

let a: number = 100, b: number = 50;

Puis mettez à jour a deux fois, une fois de la manière longue et une fois avec l’abréviation :

console.log(a);      // 100
a = a + b;
console.log(a);      // 150
console.log(a += b); // 200

Étape par étape : a commence à 100. L’instruction a = a + b lit la valeur actuelle de a, y ajoute b (50) et écrit 150 à nouveau dans a. Rien de nouveau ici par rapport à l’opérateur + vu dans la section précédente.

La dernière ligne utilise a += b, ce qui signifie exactement la même chose que a = a + b, mais de manière plus concise. Comme a vaut 150 à ce stade, sa valeur devient 200. Une affectation est également une expression dont la valeur correspond au nombre nouvellement stocké, c’est pourquoi l’exécution de console.log(a += b) affiche 200. Cela fonctionne, mais cacher un changement d’état à l’intérieur d’un argument de fonction rend le code plus difficile à suivre ; donc, dans du code réel, il est préférable d’effectuer la mise à jour sur sa propre ligne.

Le même schéma s’applique à chaque opérateur arithmétique : -=, *=, /=, %= et **= effectuent une soustraction, une multiplication, une division, un calcul du reste ou une exponentiation, puis effectuent l’affectation.

Opérateurs relationnels

Les opérateurs de comparaison examinent deux valeurs et produisent toujours une valeur booléenne, true ou false. Voici les opérandes :

let a: number = 10;
let b: number = 20;

Et les comparaisons :

console.log(a > b);   // false
console.log(a < b);   // true
console.log(a === b); // false
console.log(a >= b);  // false
console.log(a <= b);  // true
console.log(a == b);  // false
console.log(a != b);  // true

Les opérateurs de comparaison sont explicites : > (plus grand que), < (moins grand que), >= (plus grand ou égal à) et <= (moins grand ou égal à). Les opérateurs d’égalité ==, === et != vérifient si deux valeurs sont identiques, mais == et === suivent des règles différentes, et cette différence mérite une section à part entière. Le contrepartie stricte de != est !==, et c’est celui que vous devriez normalement utiliser.

Egalité lâche versus égalité stricte

Les deux opérateurs d’égalité diffèrent dans la manière dont ils traitent les types :

  • == correspond à une égalité lâche. Si les opérandes ont des types différents, il convertit silencieusement l’un d’eux avant de comparer les valeurs.
  • === représente l’égalité stricte. Il exige le même type et la même valeur, ne effectue aucune conversion, et renvoie false chaque fois que les types diffèrent.
  • Pour illustrer, prenons un nombre et une chaîne de caractères qui se ressemblent. Tous deux sont délibérément annotés comme any ; la raison en sera expliquée bientôt.

    let num1: any = 10;
    let num2: any = "10";
    

    Leur comparaison avec chacun des opérateurs donne des résultats différents :

    console.log(num1 == num2);  // true  → same value, type is ignored
    console.log(num1 === num2); // false → same value, but number ≠ string
    

    num1 contient le nombre 10 et num2 contient la chaîne de caractères "10". L’égalité lâche convertit la chaîne en nombre, trouve 10 des deux côtés et renvoie true. L’égalité stricte évite complètement cette conversion ; un nombre n’est jamais égal à une chaîne, donc le résultat est false. Comme les règles de conversion de l’égalité lâche produisent des résultats surprenants (par exemple, 0 == "" est true), la plupart des guides de style et des outils de vérification recommandent d’utiliser === et !== partout.

    Pourquoi l’exemple nécessite any

    Les annotations any ne sont pas fortuites. Si vous déclarez les variables avec leurs types réels, let num1: number = 10; et let num2: string = "10";, puis que vous écrivez num1 === num2, le code ne se compile pas. Le compilateur signale une erreur du genre :

    This comparison appears to be unintentional because the types 'number' and 'string' have no overlap.
    

    TypeScript sait qu’une valeur de type number et une valeur de type string ne peuvent jamais être identiques, c’est pourquoi il considère cette comparaison comme une erreur. Cette vérification s’applique aussi bien à == qu’à ===. En définissant les deux variables comme any, on désactive cette analyse de chevauchement, ce qui est la seule raison pour laquelle la démonstration se compile. Il s’agit d’un exemple simple mais utile montrant comment le système de types permet d’intercepter une comparaison inutile avant que le programme ne s’exécute, ce dont JavaScript pur ne donne jamais d’avertissement. Cela illustre également pourquoi any est coûteux : il désactive silencieusement ce type de protection. Pour des alternatives, consultez six alternatives sûres en termes de types pour remplacer any.

    Opérateurs logiques

    Les opérateurs logiques combinent des conditions. Il en existe trois : && (ET), || (OU) et ! (NON). Commencez avec deux valeurs booléennes :

    let b1 = true;
    let b2 = false;
    

    Puis combinez-les et néguez-les :

    console.log(b1 && b2); // false
    console.log(b1 || b2); // true
    console.log(!b2);      // true
    console.log(!b1);      // false
    

    Le comportement complet de ET et OU pour chaque paire d’entrées :

    • true et true : && donne true, || donne true.
    • true et false : && donne false, || donne true.
    • false et true : && donne false, || donne true.
    • false et false : && donne false, || donne false.

    Et pour NON :

    • !true vaut false.
    • !false vaut true.

    En termes simples :

    • && (ET) est strict : il renvoie true uniquement si les deux côtés sont true ; un seul false rend toute l’expression false.
    • || (OU) est laxiste : un seul côté true suffit, et il ne renvoie false que lorsque les deux côtés sont false.
    • ! (NON) prend un seul opérande et l’inverse : true devient false et inversement.

    Un détail à connaître : ! renvoie toujours une valeur booléenne, tandis que && et || renvoient en réalité l’un de leurs opérandes. Avec des entrées booléennes, il s’agit toujours d’une valeur booléenne, comme mentionné précédemment. Avec d’autres valeurs, "" || "default" évalue à "default" et user && user.name évalue soit à user, soit à user.name. Ces deux opérateurs fonctionnent également en mode court-circuit : si le côté gauche permet déjà de déterminer le résultat, le côté droit n’est jamais évalué. C’est cette propriété qui explique pourquoi ils sont souvent utilisés pour les valeurs par défaut et les vérifications, et TypeScript assigne en conséquence le type du résultat.

    Combinaison des comparaisons avec la logique

    Les opérateurs relationnels et logiques apparaissent généralement ensemble : les comparaisons produisent des valeurs booléennes, et les opérateurs logiques les combinent.

    console.log(20 > 10 && 20 < 10); // false
    console.log(20 > 10 || 20 < 10); // true
    

    Dans la première ligne, 20 > 10 vaut true et 20 < 10 vaut false. L’opérateur ET exige que les deux conditions soient true, donc le résultat est false.

    Dans la deuxième ligne, les deux comparaisons sont identiques, mais l’opérateur OU ne nécessite qu’une seule condition true, donc le résultat est true. Comme les opérateurs de comparaison ont une priorité plus élevée que && et ||, aucune parenthèse n’est nécessaire ici, bien que leur ajout puisse rendre les conditions plus longues plus faciles à lire.

    Incrémentation et décrémentation

    ++ ajoute 1 à un nombre et -- en soustrait 1. Chacun de ces opérateurs existe sous deux formes, préfixe et postfixe, et cette distinction est une source classique de confusion. Commencez avec une seule variable :

    let x: number = 10;
    

    Lorsque l’opérateur est utilisé seul en tant que commande, les deux formes ont le même effet :

    x++; // same as x = x + 1
    console.log(x); // 11
    ++x; // same as x = x + 1
    console.log(x); // 12
    

    Écrits sur leur propre ligne, x++ et ++x ajoutent tous deux simplement un à x. Les deux formes ne se comportent différemment que lorsque la valeur de l’expression est utilisée dans la même instruction, par exemple lorsqu’on la assigne à une autre variable. C’est là que l’incrémentation préfixe et postfixe deviennent importantes.

    Incrémentation postfixe : utiliser la valeur, puis ajouter un

    « Post » signifie après : l’expression évalue la valeur actuelle de la variable, et l’incrémentation a lieu par la suite.

    let x: number = 10;
    let res: number = x++;
    

    En affichant les deux variables, on observe l’effet :

    console.log(res); // 10
    console.log(x);   // 11
    

    Le déroulement des événements est le suivant :

    1. La valeur actuelle de x, soit 10, est lue.
    2. Cette valeur devient le résultat de l’expression et est stockée dans res.
    3. Puis x est augmenté à 11.

    Ainsi, res conserve la valeur ancienne, 10, tandis que x prend la valeur 11. L’incrémentation a bien eu lieu ; elle s’est simplement produite après que la valeur ait été copiée.

    Incrémentation avec préfixe : ajouter un, puis utiliser la valeur

    « Pre » signifie avant : la variable est d’abord incrémentée, et l’expression renvoie alors la valeur mise à jour.

    let x: number = 10;
    let res: number = ++x;
    

    Le résultat le confirme :

    console.log(res); // 11
    console.log(x);   // 11
    

    Ici, l’ordre est inversé :

    1. x passe de 10 à 11.
    2. La nouvelle valeur, 11, est stockée dans res.

    Les deux variables finissent par avoir la valeur 11. En bref : x++ renvoie la valeur avant l’incrémentation, tandis que ++x renvoie la valeur après celle-ci.

    La décrémentation suit les mêmes règles

    -- est l’inverse de ++, il soustrait au lieu d’additionner. Voici la forme avec préfixe :

    let x: number = 10;
    let res: number = --x;
    

    Et son résultat :

    console.log(res); // 9
    console.log(x);   // 9
    

    Avec la décrémentation à préfixe, x passe d’abord à 9 et cette nouvelle valeur est stockée dans res, de sorte que les deux affichent 9. La forme à suffixe x-- ferait l’inverse : res recevrait la valeur initiale 10, et ce n’est qu’alors que x descendrait à 9. De nombreuses équipes évitent complètement d’utiliser ++ et -- au sein de expressions plus complexes, justement parce que la distinction entre préfixe et suffixe est facile à mal interpréter ; x += 1 écrit sur sa propre ligne n’est jamais ambigu.

    L’opérateur conditionnel ternaire

    L’opérateur ternaire exprime un choix simple if/else sous forme d’une seule expression. Il utilise ? et :, et c’est le seul opérateur JavaScript qui prend trois opérandes, d’où vient son nom. Commencez avec deux nombres :

    let a: number = 100;
    let b: number = 200;
    

    Puis choisissez le plus grand des deux :

    let res: number = (a > b) ? a : b;
    console.log(res); // 200
    

    L’expression se compose de trois parties :

    1. (a > b) avant le ? représente la condition.
    2. a, directement après le ?, est la valeur retournée lorsque la condition est true.
    3. b, après le :, est la valeur retournée lorsque la condition est false.

    Lorsqu’on le lit à haute voix, on y lit : si a est supérieur à b, on utilise a ; sinon, on utilise b. Comme 100 n’est pas supérieur à 200, la condition est false et res devient 200.

    Remplacer un bloc if/else

    L’avantage principal réside dans la concision. La même logique écrite sous forme d’instruction occupe plusieurs lignes et nécessite de déclarer res avant de lui assigner une valeur :

    // Without ternary
    let res: number;
    if (a > b) {
      res = a;
    } else {
      res = b;
    }
    

    La version ternaire fait de même en une seule ligne. Ces deux exemples sont des alternatives, et non du code à coller dans le même contexte, car déclarer res deux fois avec let entraînerait une erreur :

    // With ternary — same result, one line
    let res: number = (a > b) ? a : b;
    

    Puisque la forme ternaire est une expression, elle permet également de déclarer la variable et de l’initialiser en même temps, et TypeScript déduit ou vérifie le type du résultat à partir des deux branches.

    Associer une valeur à une étiquette

    Les expressions ternaires fonctionnent tout aussi bien lorsque les branches ont un type différent de la condition. Cet exemple transforme un âge en une chaîne de caractères descriptive :

    let personage: number = 30;
    let res: string = (personage > 18) ? "adult" : "minor";
    console.log(res); // "adult"
    

    30 > 18 est true, donc res vaut "adulte". Pour un âge de 15, la condition serait false et res vaudrait "mineur".

    Utilisez les expressions ternaires pour des décisions simples et uniques comme celles-ci. Dès que vous commencez à imbriquer des expressions ternaires les unes dans les autres pour couvrir plusieurs cas, la lisibilité diminue rapidement, et une chaîne if...else if...else (ou un objet de recherche) est généralement plus claire.

    Points clés

    • Les opérateurs de TypeScript sont les mêmes que ceux de JavaScript ; le résultat compilé se comporte de manière identique en temps d’exécution.
  • / ne tronque jamais, % renvoie le reste, et les résultats très importants générés par ** perdent en précision à moins d’utiliser bigint.
  • Les affectations composées telles que += sont une abréviation pour « calculer puis stocker à nouveau ».
  • Préférez === et !== ; l’égalité lâche convertit les types de manières surprenantes.
  • Le compilateur rejette les comparaisons entre des types qui ne peuvent jamais coïncider, et any supprime silencieusement cette protection.
  • && et || fonctionnent en court-circuit et renvoient un opérande, c’est pourquoi ils servent également de protections et de valeurs par défaut.
  • x++ en postfixe renvoie la valeur ancienne, tandis que ++x en préfixe renvoie la nouvelle ; la différence n’apparaît que lorsque le résultat est utilisé.
  • Utilisez la forme ternaire pour un choix simple, et revenez à if...else lorsque les conditions commencent à s’accumuler.