Detaillierte Erklärung der markierten Union-Typen in TypeScript 2.0

Detaillierte Erklärung der markierten Union-Typen in TypeScript 2.0

Erstellen von Zahlungsmethoden mithilfe getaggter Union-Typen

Angenommen, wir modellieren die folgenden Zahlungsmethoden, aus denen die Benutzer unseres Systems wählen können

  • Kasse
  • PayPal mit angegebener E-Mail-Adresse
  • Kreditkarte mit Angabe der Kartennummer und Prüfziffer

Für diese Zahlungsarten können wir eine TypeScript-Schnittstelle erstellen

Schnittstelle Bargeld {
  Art: "Bargeld";
}

Schnittstelle PayPal {
  Art: "PayPal",
  E-Mail: Zeichenfolge;
}

Schnittstelle Kreditkarte {
  Art: "Kredit";
  Kartennummer: Zeichenfolge;
  Sicherheitscode: Zeichenfolge;
}

Beachten Sie, dass jeder Typ zusätzlich zu den erforderlichen Informationen über ein Artattribut verfügt, das sogenannte Diskriminanzattribut. Jeder Fall hier ist ein Zeichenfolgenliteraltyp.

Definieren Sie nun einen PaymentMethod-Typ, der die Vereinigungsmenge der drei Typen darstellt, die wir gerade definiert haben. Auf diese Weise muss jede mit PaymentMethod deklarierte Variable einen der drei angegebenen Komponententypen haben:

Typ Zahlungsmethode = Bargeld | PayPal | Kreditkarte;

Nachdem unsere Typen nun vorhanden sind, schreiben wir eine Funktion, die eine Zahlungsmethode akzeptiert und eine für Menschen lesbare Äußerung zurückgibt:

Funktion beschreibeZahlungsmethode(Methode: Zahlungsmethode) {
  Schalter (Methode.Art) {
    Fall "Bargeld":
      // Hier hat die Methode den Typ Cash
      "Bargeld" zurückgeben;

    Fall "PayPal":
      // Hier hat die Methode den Typ PayPal
      returniere `PayPal (${method.email})`;

    Fall "Kredit":
      // Hier hat die Methode den Typ CreditCard
      returniere `Kreditkarte (${method.cardNumber})`;
  }
}

Erstens enthält die Funktion nur sehr wenige Typanmerkungen, nur eine für den Methodenparameter. Ansonsten besteht die Funktion größtenteils aus reinem ES2015-Code.

In jedem Fall der Switch-Anweisung schränkt der TypeScript-Compiler den Union-Typ auf einen seiner Membertypen ein. Beispielsweise wird bei der Übereinstimmung mit „paypal“ der Typ des Methodenparameters von PaymentMethod auf PayPal eingegrenzt. Daher können wir auf die E-Mail-Eigenschaft zugreifen, ohne eine Typbehauptung hinzufügen zu müssen.

Im Wesentlichen verfolgt der Compiler den Programmkontrollfluss, um getaggte Union-Typen einzugrenzen. Dabei werden neben der Switch-Anweisung auch die Bedingung und die Auswirkungen von Zuweisung und Rückgabe berücksichtigt.

Funktion beschreibeZahlungsmethode(Methode: Zahlungsmethode) {
  if (Methode.Art === "Bargeld") {
    // Hier hat die Methode den Typ Cash
    "Bargeld" zurückgeben;
  }

  // Hier hat die Methode den Typ PayPal | CreditCard

  if (Methode.Art === "paypal") {
    // Hier hat die Methode den Typ PayPal
    returniere `PayPal (${method.email})`;
  }

  // Hier hat die Methode den Typ CreditCard
  returniere `Kreditkarte (${method.cardNumber})`;
}

Durch die Kontrollflusstypanalyse wird die Verwendung getaggter Union-Typen zum Kinderspiel. Mit minimalem TypeScript-Syntax-Overhead können wir nahezu reines JavaScript schreiben und dennoch von der Typprüfung und Codevervollständigung profitieren.

Erstellen von Redux-Aktionen mit getaggten Union-Typen

Ein Anwendungsfall, bei dem getaggte Union-Typen wirklich zur Geltung kommen, ist die Verwendung von Redux in einer TypeScript-Anwendung. Erstellen wir ein Beispiel, das ein Modell, zwei Aktionen und einen Reducer für eine Todo-Anwendung enthält.

Nachfolgend sehen Sie einen vereinfachten Todo-Typ, der ein einzelnes Todo darstellt. Der Modifikator „readonly“ wird hier verwendet, um zu verhindern, dass die Eigenschaft geändert wird.

Schnittstelle Todo {
  schreibgeschützter Text: Zeichenfolge;
  schreibgeschützt, fertig: Boolesch;
}

Benutzer können neue Aufgaben hinzufügen und den Erledigungsstatus vorhandener Aufgaben umschalten. Gemäß diesen Anforderungen benötigen wir zwei Redux-Operationen, und zwar wie folgt:

Schnittstelle AddTodo {
  Typ: "ADD_TODO";
  Text: Zeichenfolge;
}

Schnittstelle ToggleTodo {
  Typ: "TOGGLE_TODO";
  Index: Nummer
}

Wie im vorherigen Beispiel können Sie jetzt eine Redux-Aktion als Vereinigung aller von Ihrer Anwendung unterstützten Aktionen erstellen:

Typ ReduxAction = AddTodo | ToggleTodo;

In diesem Fall fungiert die Typ-Eigenschaft als diskriminierendes Attribut und folgt einem gängigen Benennungsmuster in Redux. Fügen Sie nun einen Reducer hinzu, der mit diesen beiden Aktionen funktioniert:

Funktion todosReducer(
  Status: ReadonlyArray<Todo> = [],
  Aktion: ReduxAction
): Nur-Lese-Array<Todo> {
  Schalter (Aktion.Typ) {
    Fall "ADD_TODO":
      // Aktion hat hier den Typ AddTodo
      return [...Status, {Text: Aktion.Text, erledigt: false}];

    Fall "TOGGLE_TODO":
      // Aktion hat hier den Typ ToggleTodo
      returniere Status.map((todo, index) => {
        wenn (index !== aktion.index) {
          Aufgabe zurückgeben;
        }

        zurückkehren {
          Text: todo.text,
          erledigt: !todo.done
        };
      });

    Standard:
      Rückgabezustand;
  }
}

Ebenso enthalten nur Funktionssignaturen Typanmerkungen. Der restliche Code ist reines ES2015 und nicht TypeScript-spezifisch.

Wir folgen der gleichen Logik wie im vorherigen Beispiel. Basierend auf der Typ-Eigenschaft der Redux-Aktion berechnen wir den neuen Status, ohne den vorhandenen Status zu ändern. Im Fall der Switch-Anweisung können wir ohne Typzusicherungen auf die für jeden Operationstyp spezifischen Text- und Indexeigenschaften zugreifen.

Der „Niemals“-Typ

TypeScript 2.0 führt einen neuen primitiven Typ ein, niemals. Der Typ „Nie“ gibt an, dass der Wert dieses Typs nie vorkommt. Insbesondere ist „never“ der Rückgabetyp einer Funktion, die niemals zurückkehrt, und es ist auch der Typ einer Variablen, der in einem Typschutz niemals wahr ist.

Dies sind die genauen Merkmale des Typs „never“, wie unten beschrieben:

  • „Never“ ist ein Untertyp aller Typen und kann allen Typen zugewiesen werden.
  • Kein Typ ist ein Untertyp von „never“ oder kann „never“ zugewiesen werden (mit Ausnahme des Typs „never“ selbst).
  • Wenn ein Funktionsausdruck oder eine Pfeilfunktion keine Rückgabetypannotation hat, wenn die Funktion keine Return-Anweisung oder nur eine Return-Anweisung vom Typ „never“ hat und wenn die Funktion nicht bis zu einem Endpunkt ausführbar ist (wie beispielsweise durch eine Kontrollflussanalyse ermittelt), dann ist der abgeleitete Rückgabetyp der Funktion „never“.
  • In einer Funktion mit einer expliziten „Never“-Return-Typannotation müssen alle Return-Anweisungen (sofern vorhanden) Ausdrücke vom Typ „Never“ enthalten und der Endpunkt der Funktion darf nicht ausführbar sein.

Ich bin verwirrt von dem, was ich gehört habe. Als nächstes werde ich anhand einiger Beispiele über diesen großen Bruder Never sprechen.

Funktionen, die nie zurückkehren

Hier ist ein Beispiel für eine Funktion, die nie etwas zurückgibt:

// Typ () => nie
const sing = Funktion() {
  während (wahr) {
    console.log("Ich gebe einfach keinen Wert zurück, na und!");
    console.log("Ich gebe einfach keinen Wert zurück, na und!");
    console.log("Ich gebe einfach keinen Wert zurück, na und!");
    console.log("Ich gebe einfach keinen Wert zurück, na und!");
    console.log("Ich gebe einfach keinen Wert zurück, na und!");
    console.log("Ich gebe einfach keinen Wert zurück, na und!");
  }
}

Die Funktion besteht aus einer Endlosschleife, die keine Break- oder Return-Anweisungen enthält. Es gibt daher keine Möglichkeit, aus der Schleife auszusteigen. Daher ist der abgeleitete Rückgabetyp der Funktion „nie“.

In ähnlicher Weise wird der Rückgabetyp der folgenden Funktion als nie abgeleitet

// Typ (Nachricht: Zeichenfolge) => nie
const failwith = (Nachricht: Zeichenfolge) => {
  wirf einen neuen Fehler (Nachricht);
};

TypeScript leitet den Typ „never“ ab, da die Funktion weder eine Rückgabetypannotation noch einen erreichbaren Endpunkt hat (wie durch die Kontrollflussanalyse ermittelt).

Es ist nicht möglich, eine Variable dieses Typs zu haben

Alternativ wird davon ausgegangen, dass der Typ „nie“ niemals wahr ist. Im folgenden Beispiel prüfen wir, ob der Wertparameter sowohl eine Zeichenfolge als auch eine Zahl ist, was unmöglich ist.

Funktion impossibleTypeGuard(Wert: beliebig) {
  Wenn (
    Typ des Wertes === "Zeichenfolge" &&
    Typ des Wertes === "Zahl"
  ) {
    Wert; // Typ nie
  }
}

Dieses Beispiel ist offensichtlich übermäßig konstruiert, schauen wir uns also einen praktischeren Anwendungsfall an. Das folgende Beispiel zeigt die Kontrollflussanalyse von TypeScript, die den Union-Typ der Variablen unter dem Typschutz einschränkt. Intuitiv weiß der Typprüfer, dass der Wert, sobald wir geprüft haben, dass es sich um eine Zeichenfolge handelt, keine Zahl sein kann und umgekehrt.

Funktion controlFlowAnalysisWithNever(
  Wert: Zeichenfolge | Zahl
) {
  wenn (Typ des Wertes === "Zeichenfolge") {
    Wert; // Typ Zeichenfolge
  } sonst wenn (Typ des Wertes === "Zahl") {
    Wert; // Typnummer
  } anders {
    Wert; // Typ nie
  }
}

Beachten Sie, dass der Wert im letzten Else-Zweig weder eine Zeichenfolge noch eine Zahl sein kann. In diesem Fall leitet TypeScript den Typ „never“ ab, da wir den Wertparameter als Typ „string | number“ annotiert haben, was bedeutet, dass der Wertparameter keinen anderen Typ außer „string“ oder „number“ haben kann.

Sobald die Kontrollflussanalyse „String“ und „Number“ als Kandidaten für den Werttyp ausgeschlossen hat, leitet der Typprüfer den Typ „Never“ ab, der die einzige verbleibende Möglichkeit darstellt. Allerdings können wir mit dem Wert nichts Sinnvolles anfangen, da sein Typ „nie“ ist. Daher zeigt unser Editor-Tool nicht automatisch an, welche Methoden oder Eigenschaften für den Wert verfügbar sind.

Der Unterschied zwischen „nie“ und „nichtig“

Sie fragen sich vielleicht, warum TypeScript einen „Never“-Typ benötigt, wenn es bereits einen „Void“-Typ hat. Obwohl die beiden Konzepte ähnlich erscheinen, handelt es sich um zwei unterschiedliche Konzepte:

Funktionen ohne expliziten Rückgabewert geben implizit „undefined“ zurück. Obwohl wir normalerweise sagen würden, dass eine solche Funktion „nichts zurückgibt“, gibt sie etwas zurück. In diesen Fällen ignorieren wir normalerweise den Rückgabewert. In TypeScript wird davon ausgegangen, dass solche Funktionen den Rückgabetyp „void“ haben.

Eine Funktion mit einem „Niemals zurückkehren“-Typ kehrt nie zurück. Es gibt auch nicht „undefiniert“ zurück. Die Funktion wird nicht normal abgeschlossen, was bedeutet, dass ein Fehler ausgegeben wird oder die Ausführung überhaupt nicht beendet wird.

Typinferenz für Funktionsdeklarationen

Es gibt ein kleines Problem bezüglich der Rückgabetypinferenz für Funktionsdeklarationen. Wenn Sie sich die verschiedenen Nie-Funktionen ansehen, die wir zuvor aufgelistet haben, werden Sie den folgenden Satz finden:

Wenn ein Funktionsausdruck oder eine Pfeilfunktion keine Rückgabetypannotation hat, wenn die Funktion keine Return-Anweisung oder nur eine Return-Anweisung vom Typ „never“ hat und wenn die Funktion nicht bis zu einem Endpunkt ausführbar ist (wie beispielsweise durch eine Kontrollflussanalyse ermittelt), dann ist der abgeleitete Rückgabetyp der Funktion „never“.

Es erwähnt Funktionsausdrücke und Pfeilfunktionen, aber keine Funktionsdeklarationen. Das bedeutet, dass der für einen Funktionsausdruck abgeleitete Rückgabetyp sich von dem für eine Funktionsdeklaration abgeleiteten Rückgabetyp unterscheiden kann:

// Rückgabetyp: void
Funktion failwith1(Nachricht: Zeichenfolge) {
  wirf einen neuen Fehler (Nachricht);
}

// Rückgabetyp: nie
const failwith2 = Funktion(Nachricht: Zeichenfolge) {
  wirf einen neuen Fehler (Nachricht);
};

Der Grund für dieses Verhalten ist die Abwärtskompatibilität, wie unten beschrieben. Wenn Sie möchten, dass eine Funktionsdeklaration den Rückgabetyp „nie“ hat, können Sie dies explizit kommentieren:

Funktion failwith1(Nachricht: Zeichenfolge): nie {
  wirf einen neuen Fehler (Nachricht);
}

Oben finden Sie eine ausführliche Erläuterung der getaggten Union-Typen von TypeScript 2.0. Weitere Informationen zu getaggten Union-Typen von TS 2.0 finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Tiefgreifendes Verständnis der Verwendung des Schlüsselworts „infer“ in Typescript
  • Warum TypeScripts Enum problematisch ist
  • Ein Tutorial zur Installation, Verwendung und automatischen Kompilierung von TypeScript
  • Der neue TypeScript-Schnellstart-Übungsbericht des Partners Vue
  • So begrenzen Sie den Wertebereich von Objektschlüsseln in TypeScript
  • Erklären Sie TypeScript-zugeordnete Typen und eine bessere wörtliche Typinferenz.
  • Eine kurze Diskussion über 3 bemerkenswerte neue Features in TypeScript 3.7
  • Tutorial zur TypeScript-Funktionsdefinition und zu Anwendungsfällen

<<:  Zusammenfassung der mit dem Abfangen von MySQL-Zeichenfolgen verbundenen Funktionen

>>:  Detailliertes Tutorial zur Installation von MySQL 5.7.26 auf CentOS7.4

Artikel empfehlen

Details zur Verwendung des JSON-Typs in MySQL 5.7

JSON ist ein leichtes Datenaustauschformat, das e...

Lösen Sie das MySQL 5.7.9 Version sql_mode=only_full_group_by Problem

MySQL 5.7.9 Version sql_mode=only_full_group_by P...

Detaillierte Erläuterung der Nginx-Anti-Hotlink- und Anti-Crawler-Konfiguration

Erstellen Sie eine neue Konfigurationsdatei (gehe...

js-Methode zur Realisierung der Warenkorbberechnung

In diesem Artikelbeispiel wird der spezifische Co...

Prozessdiagramm zur Implementierung der Zabbix WEB-Überwachung

Nehmen Sie als Beispiel die WEB-Schnittstelle von...

Analyse des Sperrmechanismus der MySQL-Datenbank

Bei gleichzeitigen Zugriffen kann es zu nicht wie...

Welche Szenarien sind für JS-Pfeilfunktionen nicht geeignet?

Inhaltsverzeichnis Überblick Definieren von Metho...

Auszeichnungssprache - vereinfachte Tags

Klicken Sie hier, um zum Abschnitt „HTML-Tutorial“...

CentOS 7 kann nach dem Ändern der Netzwerkkarte nicht auf das Internet zugreifen

Ping www.baidu.com unbekannter Domänenname Ändern...

Verwendung von TypeScript-Generics

Inhaltsverzeichnis 1. Einfach zu bedienen 2. Verw...

Detaillierte Erläuterung des Quellcodes der vue.$set()-Methode von Vue

Bei der Verwendung von Vue zum Entwickeln von Pro...