Startseite / Artikel / TypScript-Operatoren in der Praxis: Mathematik, Vergleiche, Logik und Ternäre

TypScript-Operatoren in der Praxis: Mathematik, Vergleiche, Logik und Ternäre

Eine praktische Übersicht über die arithmetischen, Zuweisungs-, Vergleichs-, logischen, Inkrementier- und ternären Operatoren von TypeScript, einschließlich der Gleichheitsprüfung, die vom Compiler abgelehnt wird.

2635 Wörter

Sobald Sie wissen, wie TypeScript Typen zuweist und durchsetzt, ist der nächste Schritt, etwas mit den Werten anzustellen: Sie zu addieren, miteinander zu vergleichen, Bedingungen zu kombinieren und zwischen Alternativen zu wählen. Operator sind die Symbole, die diese Aufgaben ausführen, und die meisten verhalten sich genauso wie in JavaScript. Einige verbergen jedoch Details, die häufig zu Fehlern führen, wie beispielsweise loose versus strict Equality sowie der Unterschied zwischen x++ und ++x.

Dieser Leitfaden behandelt arithmetische, Zuweisungs-, relationale, logische, Inkrementier- und Dekrementieroperator sowie ternäre Operator mit kleinen Beispielen, über die Sie ohne Ausführung nachdenken können. Dabei werden Sie erkennen, wo der TypeScript-Compiler ein Sicherheitsnetz hinzufügt, das normales JavaScript nicht bietet.

Zuerst ein Shortcut: mehrere Deklarationen in einer Anweisung

Bevor wir uns den Operatoren zuwenden, gibt es eine syntaktische Erleichterung, auf die die späteren Beispiele zurückgreifen. Die übliche Art, zwei Zahlen zu deklarieren, besteht darin, pro Variable eine Anweisung zu verwenden:

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

Mit einem einzigen let-Befehl können auch mehrere Variablen auf einmal eingeführt werden, sofern man sie mit Kommas voneinander trennt:

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

Beide Formen führen zum gleichen Ergebnis; die zweite ist einfach kürzer. Jede Variable in der Liste besitzt ihre eigene Anmerkung, wodurch sie unterschiedliche Typen haben können:

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

Verwenden Sie diese Form sparsam. Sie eignet sich gut für ein Paar eng miteinander verbundener Werte, wie a und b, die in diesem Leitfaden verwendet werden, doch eine Zeile, die einen Zähler, einen Namen und ein Flag mischt, ist schwieriger zu überblicken als drei getrennte Deklarationen.

Aritmetische Operatoren

Dies sind die bekannten Symbole aus der Schulmathematik. Die untenstehenden Beispiele beginnen mit zwei Zahlen:

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

Jede Zeile wendet anschließend einen Operator an und zeigt das Ergebnis in einem Kommentar an:

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)

Was jeder Operator bewirkt:

  • + addiert die beiden Operanden.
  • - subtrahiert den rechten Operanden vom linken.
  • * multipliziert sie miteinander.
  • / teilt sie ohne Abschätzung. 10 / 20 ist tatsächlich 0.5; es gibt keine separate Ganzzahltrennung wie in einigen anderen Sprachen, da jede JavaScript-Zahl einen Gleitkommawert darstellt.
  • % gibt den Rest zurück. 10 % 20 ist 10: Zwanzig passt null Mal in zehn, daher bleibt die gesamte Zehn übrig.
  • ** erhebt den linken Operanden zur Potenz des rechten. 10 ** 20 bedeutet, zehn mit sich selbst zwanzig Mal zu multiplizieren, was das sehr lange Ergebnis erklärt.

Eine Vorsichtsmaßnahme bei ** und großen Ergebnissen: Zahlen über Number.MAX_SAFE_INTEGER (ungefähr neun Quadrillionen) verlieren ihre Ganzzahlprecision. 10 ** 20 wird zufällig genau ausgegeben, doch die Arithmetik mit Werten dieser Größe ist nicht zuverlässig; verwenden Sie bigint, wenn Sie genaue große Ganzzahlen benötigen.

Zuweisung und zusammengesetzte Zuweisung

Die einfache =-Operator speichert einen Wert in einer Variablen. JavaScript und TypeScript bieten außerdem zusammengesetzte Zuweisungsoperatoren, die einen arithmetischen Schritt ausführen und das Ergebnis in einer einzigen Ausdrucksformulierung speichern. Beginnen Sie mit zwei Werten:

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

Dann aktualisieren Sie a zweimal – einmal auf die lange Weise und einmal mit der Abkürzung:

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

Schritt für Schritt: a beginnt bei 100. Der Ausdruck a = a + b liest den aktuellen Wert von a, addiert b (50) und schreibt 150 wieder in a zurück. Hier gibt es nichts Neues außer dem + -Operator aus dem vorherigen Abschnitt.

Die letzte Zeile verwendet a += b, was genau dasselbe bedeutet wie a = a + b, nur kompakter geschrieben. Da a zu diesem Zeitpunkt 150 ist, wird der Wert auf 200 erhöht. Eine Zuweisung ist ebenfalls ein Ausdruck, dessen Wert die neu gespeicherte Zahl ist; deshalb gibt console.log, dem a += b direkt übergeben wird, 200 aus. Das funktioniert zwar, doch das Verbergen einer Zustandsänderung innerhalb eines Funktionsarguments erschwert die Lesbarkeit des Codes. Deshalb sollte man in echtem Code die Aktualisierung lieber auf einer eigenen Zeile durchführen.

Das gleiche Muster gilt für jeden arithmetischen Operator: -=, *=, /=, %= und **= subtrahieren, multiplizieren, teilen, berechnen den Rest oder erheben zur Potenz und weisen das Ergebnis anschließend zu.

Relationale Operatoren

Vergleichsoperatoren prüfen zwei Werte und liefern stets ein boolesches Ergebnis, true oder false. Hier sind die Operanden:

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

Und die Vergleiche:

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

Die Sortieroperatoren sind selbst erklärend: > (größer als), < (kleiner als), >= (größer oder gleich) und <= (kleiner oder gleich). Die Gleichheitsoperatoren ==, === und != prüfen, ob zwei Werte übereinstimmen, wobei == und === unterschiedliche Regeln befolgen – dieser Unterschied verdient eigene Erklärung. Der strenge Gegenpart von != ist !==, und dieser sollte in der Regel verwendet werden.

Laxe Gleichheit versus strenge Gleichheit

Die beiden Gleichheitsoperatoren unterscheiden sich in der Art und Weise, wie sie Typen behandeln:

  • == ist laxe Gleichheit. Wenn die Operanden unterschiedliche Typen haben, wird einer von ihnen stillschweigend umgewandelt, bevor die Werte verglichen werden.
  • === steht für strenge Gleichheit. Es erfordert denselben Typ und denselben Wert, führt keine Umwandlung durch und gibt false zurück, sobald die Typen unterschiedlich sind.
  • Zur Veranschaulichung nehmen wir eine Zahl und einen String, die ähnlich aussehen. Beide sind absichtlich als any gekennzeichnet; der Grund wird gleich erläutert.

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

    Der Vergleich mit jedem Operator liefert unterschiedliche Ergebnisse:

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

    num1 enthält die Zahl 10 und num2 enthält den String "10". Die lockere Gleichheit wandelt den String in eine Zahl um, findet auf beiden Seiten 10 und gibt true zurück. Die strenge Gleichheit überspringt die Umwandlung ganz; eine Zahl ist niemals gleich einem String, daher ist das Ergebnis false. Da die Umwandlungsregeln der lockeren Gleichheit überraschende Ergebnisse liefern (zum Beispiel ist 0 == "" true), empfehlen die meisten Stilrichtlinien und Linter, überall === und !== zu verwenden.

    Warum das Beispiel any benötigt

    Die any-Anmerkungen sind nicht zufällig. Wenn Sie die Variablen mit ihren tatsächlichen Typen deklarieren, let num1: number = 10; und let num2: string = „10“;, und anschließend num1 === num2 schreiben, kompiliert der Code nicht. Der Compiler meldet einen Fehler in etwa folgendem Sinne:

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

    TypeScript weiß, dass ein Wert vom Typ number und ein Wert vom Typ string niemals identisch sein können, weshalb es den Vergleich als Fehler betrachtet. Die Überprüfung gilt sowohl für == als auch für ===. Wenn beide Variablen als any deklariert werden, wird diese Überlappungsanalyse deaktiviert – das ist der einzige Grund, warum die Demonstration kompiliert. Es handelt sich um ein kleines, aber nützliches Beispiel dafür, wie das Typsystem sinnlose Vergleiche bereits vor dem Ausführen des Programms erkennt, wofür reines JavaScript nie warnt. Es zeigt außerdem, warum any kostspielig ist: Es deaktiviert stillschweigend genau diese Art von Schutz. Für Alternativen siehe sechs typsichere Ersatzlösungen für any.

    Logische Operatoren

    Logische Operatoren kombinieren Bedingungen. Es gibt drei davon: && (UND), || (ODER) und ! (NICHT). Beginnen Sie mit zwei Booleschen Werten:

    let b1 = true;
    let b2 = false;
    

    Dann kombinieren und verneinen Sie sie:

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

    Das vollständige Verhalten von AND und OR für jedes Eingabepaar:

    • true und true: && ergibt true, || ergibt true.
    • true und false: && ergibt false, || ergibt true.
    • false und true: && ergibt false, || ergibt true.
    • false und false: && ergibt false, || ergibt false.

    Für NOT:

    • !true ist false.
    • !false ist true.

    In Worten:

    • && (UND) ist streng. Es gibt true nur zurück, wenn beide Seiten true sind; bereits eine false macht die gesamte Ausdrucksformel zu false.
    • || (ODER) ist nachsichtig. Schon eine true-Seite reicht aus, und es gibt false nur zurück, wenn beide Seiten false sind.
    • ! (NICHT) nimmt einen einzigen Operanden und invertiert ihn: true wird zu false und umgekehrt.

    Ein Detail, das man wissen sollte: ! gibt immer ein Boolesches Wert zurück, während && und || tatsächlich einen ihrer Operanden zurückgeben. Bei booleschen Eingaben ist das wie oben immer ein Boolescher Wert. Bei anderen Werten ergibt "" || "default" den Wert "default", und user && user.name ergibt entweder user oder user.name. Beide Operator arbeiten außerdem schräg: Wenn die linke Seite bereits das Ergebnis bestimmt, wird die rechte Seite nie ausgewertet. Aufgrund dieses Verhaltens werden sie häufig für Standardwerte und Schutzmechanismen verwendet, und TypeScript typisiert das Ergebnis entsprechend.

    Kombination von Vergleichen mit Logik

    Relationale und logische Operator treten in der Regel zusammen auf: Vergleiche erzeugen Boolesche Werte, und logische Operator kombinieren diese.

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

    In der ersten Zeile ist 20 > 10 true und 20 < 10 ist false. AND erfordert, dass beide Werte true sind, daher ist das Ergebnis false.

    In der zweiten Zeile sind die beiden Vergleiche identisch, doch OR benötigt nur einen true-Wert, weshalb das Ergebnis true ist. Da Vergleichsoperatoren enger gebunden sind als && und ||, sind hier keine Klammern notwendig, obwohl ihr Einsatz längere Bedingungen übersichtlicher machen kann.

    Erhöhen und Verringern

    ++ fügt einer Zahl 1 hinzu und -- subtrahiert 1. Beide Operatoren existieren in zwei Formen, Präfix- und Postfixform, wobei der Unterschied eine klassische Quelle von Verwirrung ist. Beginnen Sie mit einer einzigen Variablen:

    let x: number = 10;
    

    Wenn der Operator als eigenständige Anweisung verwendet wird, haben beide Formen denselben Effekt:

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

    Sollte x++ und ++x auf eigenen Zeilen geschrieben werden, fügen sie beide einfach eins zu x hinzu. Die beiden Formen unterscheiden sich nur dann in ihrem Verhalten, wenn der Wert der Expressions im selben Satz verwendet wird – beispielsweise bei einer Zuweisung an eine andere Variable. Genau da werden Präfix- und Postfix-Operationen relevant.

    Postfix-Inkrement: Zuerst der Wert nutzen, anschließend eins addieren

    „Post“ bedeutet nach: Die Expressions wird zum aktuellen Wert der Variable ausgewertet, und das Inkrement findet danach statt.

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

    Das Ausgeben beider Variablen zeigt die Wirkung:

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

    Die Reihenfolge der Ereignisse ist:

    1. Der aktuelle Wert von x, nämlich 10, wird abgerufen.
    2. Der Wert wird zum Ergebnis der Expressions und in res gespeichert.
    3. Anschließend wird x auf 11 erhöht.

    Daher behält res den alten Wert, 10, während x auf 11 wechselt. Die Erhöhung hat tatsächlich stattgefunden; sie erfolgte lediglich nachdem der Wert kopiert wurde.

    Vorzeichenbehaftete Erhöhung: Einen hinzufügen und anschließend den Wert verwenden

    "Pre" bedeutet vorher: Zuerst wird die Variable erhöht, und der Ausdruck ergibt den aktualisierten Wert.

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

    Die Ausgabe bestätigt dies:

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

    Hier ist die Reihenfolge umgekehrt:

    1. x wechselt von 10 auf 11.
    2. Der neue Wert, 11, wird in res gespeichert.

    Sowohl Variablen enden schließlich bei 11. Kurz gesagt: x++ liefert den Wert vor der Erhöhung, ++x den Wert danach.

    Die Verringerung folgt denselben Regeln

    -- spiegelt ++ wider, indem es statt addieren subtrahiert. Hier ist die Präfixform:

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

    Und deren Ausgabe:

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

    Mit der Präfix-Dekrementierung sinkt x zunächst auf 9, und dieser neue Wert wird in res gespeichert, sodass beide 9 ausgeben. Die Postfix-Form x-- würde das Gegenteil tun: res würde den ursprünglichen Wert 10 erhalten, und erst danach würde x auf 9 sinken. Viele Teams vermeiden es ganz, ++ und -- in größeren Ausdrücken zu verwenden, genau weil die Unterscheidung zwischen Präfix und Postfix leicht falsch interpretiert werden kann; x += 1 in einer eigenen Zeile ist niemals zweideutig.

    Der ternäre bedingte Operator

    Der ternäre Operator drückt eine einfache if/else-Entscheidung als einzige Ausdrucksform aus. Er verwendet ? und : und ist der einzige JavaScript-Operator, der drei Operanden benötigt – daher auch sein Name. Beginnen wir mit zwei Zahlen:

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

    Dann wählen wir die größere aus:

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

    Der Ausdruck besteht aus drei Teilen:

    1. (a > b) vor dem ? ist die Bedingung.
    2. a, direkt nach dem ?, ist das Ergebnis, wenn die Bedingung true ist.
    3. b, nach dem :, ist das Ergebnis, wenn die Bedingung false ist.

    Wenn man es laut vorliest, steht da: Wenn a größer als b ist, verwendet man a, ansonsten b. Da 100 nicht größer als 200 ist, gilt die Bedingung als false und res erhält den Wert 200.

    Ersatz für ein if/else-Block

    Der Hauptvorteil liegt in der Kürze. Derselbe Logikausdruck, geschrieben als Aussage, benötigt mehrere Zeilen und erfordert die Deklaration von res, bevor dieser zugewiesen werden kann:

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

    Die ternäre Version erledigt dasselbe in einer Zeile. Die beiden Beispiele sind Alternativen und nicht dazu bestimmt, in denselben Kontext eingefügt zu werden, da eine doppelte Deklaration von res mit let zu einem Fehler führen würde:

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

    Weil die ternäre Logik ein Ausdruck ist, ermöglicht sie es außerdem, die Variable gleichzeitig zu deklarieren und initialisieren. TypeScript erkennt oder überprüft dabei den Typ des Ergebnisses aus beiden Ästen.

    Eine Wert zu einem Etikett zuordnen

    Ternäre Operatoren funktionieren genauso gut, wenn die Zweige einen anderen Typ als die Bedingung haben. Dieses Beispiel wandelt ein Alter in einen beschreibenden String um:

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

    30 > 18 ergibt true, daher ist res "adult". Bei einem Alter von 15 wäre die Bedingung false und res wäre "minor".

    Bewahren Sie Ternäre Operatoren für solche einfachen, einzelnen Entscheidungen auf. Sobald Sie anfangen, einen Ternären Operator in einen anderen zu versetzen, um mehrere Fälle abzudecken, sinkt die Lesbarkeit schnell – in solchen Fällen ist eine if...else if...else-Kette (oder ein Abfrageobjekt) in der Regel klarer.

    Hauptpunkte

    • Die Operatoren von TypeScript sind dieselben wie die von JavaScript; die kompilierte Ausgabe verhält sich zur Laufzeit identisch.
  • / kürzt niemals ab, % gibt den Restwert zurück, und sehr große Ergebnisse von ** verlieren an Präzision, es sei denn, man verwendet bigint.
  • Komplexe Zuweisungen wie += sind Abkürzungen für „berechnen und zurückspeichern“.
  • Verwenden Sie lieber === und !==; die lockere Gleichheit konvertiert Typen auf überraschende Weise.
  • Der Compiler lehnt Vergleiche zwischen Typen ab, die niemals übereinanderfallen können, und any entfernt diesen Schutz stillschweigend.
  • && und || arbeiten kurzgeschlossen und geben einen Operanden zurück, weshalb sie auch als Schutzmechanismen und Standardwerte dienen können.
  • Das Postfix x++ gibt den alten Wert aus, das Prefix ++x den neuen; der Unterschied zeigt sich erst dann, wenn das Ergebnis verwendet wird.
  • Verwenden Sie die ternäre Logik für eine klare Entscheidung und wechseln Sie zurück zu if...else, sobald sich die Bedingungen häufen.