Detaillierte Erklärung der numerischen Trennzeichen von TS und strengere Klassenattributprüfungen

Detaillierte Erklärung der numerischen Trennzeichen von TS und strengere Klassenattributprüfungen

Überblick

TypeScript 2.4 implementiert eine Rechtschreibkorrektur für Bezeichner. Selbst wenn wir eine Variable, eine Eigenschaft oder einen Funktionsnamen leicht falsch schreiben, kann TypeScript in vielen Fällen die richtige Schreibweise vorschlagen.

TypeScript 2.7 unterstützt den ECMAScript-Vorschlag für Zahlentrennzeichen. Mit dieser Funktion können Benutzer Zahlen gruppieren, indem sie Unterstriche (_) zwischen ihnen verwenden (genauso wie sie Kommas und Punkte zum Gruppieren von Zahlen verwenden).

const Weltbevölkerung im Jahr 2017 = 7_600_000_000;
const leastSignificantByteMask = 0b1111_1111;
const papayawhipColorHexCode = 0xFF_EF_D5;

Die Zifferntrennzeichen ändern den Wert des numerischen Literals nicht, aber die Gruppierung sorgt dafür, dass die Zahl für den Benutzer auf einen Blick leichter lesbar ist.

Diese Trennzeichen sind auch für Binär- und Hexadezimalzahlen nützlich.

sei Bits = 0b0010_1010;
lass Routine = 0xC0FFEE_F00D_BED;
lass martin = 0xF0_1E_

Beachten Sie, dass sich Zahlen in JavaScript nicht für Kreditkarten- und Telefonnummern eignen, auch wenn es nicht intuitiv erscheint. Zeichenfolgen sind hierfür besser geeignet.

Wenn wir das Ziel auf den obenstehenden, von es2015 kompilierten Code setzen, generiert TypeScript den folgenden JS-Code:

const WeltbevölkerungIn2017 = 7600000000;
const leastSignificantByteMask = 255;
const papayawhipColorHexCode = 16773077;

in Operatorverfeinerung und präziser Instanziierung

TypeScript 2.7 bringt zwei Änderungen zur Typverfeinerung – die Möglichkeit, detailliertere Typen durch die Durchsetzung von „Typwächtern“ zu bestimmen.

Erstens verwendet der Operator „instanceof“ jetzt die Vererbungskette, anstatt sich auf strukturelle Kompatibilität zu verlassen, was das Verhalten des Operators „instanceof“ zur Laufzeit genauer widerspiegelt. Dies kann dazu beitragen, einige Komplikationen bei der Verwendung von „instanceof“ zum Verfeinern strukturell ähnlicher (aber nicht verwandter) Typen zu vermeiden.

Zweitens fungiert der In-Operator jetzt als Typwächter und schränkt Eigenschaftsnamen ein, die nicht explizit deklariert sind.

Schnittstelle A { a: Zahl };
Schnittstelle B { b: Zeichenfolge };

Funktion foo(x: A | B) {
    wenn ("a" in x) {
        Rückgabe xa;
    }
    Rückgabe xb;
}

Intelligentere Objektliteral-Inferenz

Es gibt ein Muster in JS, bei dem Benutzer einige Eigenschaften weglassen und der Wert dieser Eigenschaften bei der späteren Verwendung undefiniert ist.

lass foo = irgendeinTest ? { Wert: 42 } : {};

Zuvor suchte TypeScript nach dem besten Supertyp von { value: number } und {}, was {} war. Dies ist technisch korrekt, aber nicht sehr nützlich.

Ab Version 2.7 „normalisiert“ TypeScript jeden Objektliteraltypdatensatz für jede Eigenschaft, fügt für jede undefinierte Typeneigenschaft eine optionale Eigenschaft ein und vereint sie.

Im obigen Beispiel ist der endgültige Typ von foo { value: number } | { value?: undefined }. In Kombination mit der feinkörnigen Typisierung von TypeScript können wir ausdrucksstärkeren Code schreiben, den TypeScript verstehen kann. Schauen wir uns ein weiteres Beispiel an:

// Hat Typ
// | { a: Boolescher Wert, aData: Zahl, b?: nicht definiert }
// | { b: boolean, bData: string, a?: nicht definiert }
lass bar = Math.random() < 0,5?
    { a: true, aData: 100 } :
    { b: true, bData: "hallo" };

wenn (bar.b) {
    // TypeScript weiß jetzt, dass „bar“ den Typ hat
    //
    // '{ b: boolean, bData: string, a?: nicht definiert }'
    //
    // damit es weiß, dass „bData“ verfügbar ist.
    bar.bData.toLowerCase()
}

Hier kann TypeScript den Balkentyp durch Überprüfen der b-Eigenschaft verfeinern und uns dann Zugriff auf die bData-Eigenschaft gewähren.

Eindeutiger Symboltyp und konstante Namensattribute

TypeScript 2.7 verfügt über ein tieferes Verständnis der ECMAScript-Symbole und bietet Ihnen mehr Flexibilität bei deren Verwendung.

Ein häufig nachgefragter Anwendungsfall ist die Verwendung von Symbolen zum Deklarieren einer gut typisierten Eigenschaft. Betrachten Sie beispielsweise das folgende Beispiel:

const Foo = Symbol("Foo");
const Bar = Symbol("Balken");

sei x = {
    [Foo]: 100,
    [Bar]: "hallo",
};

let a = x[Foo]; // hat Typ 'Zahl'
lass b = x[Bar]; // hat Typ 'string'

Wie Sie sehen, kann TypeScript verfolgen, dass x Eigenschaften hat, die mit den Symbolen Foo und Bar deklariert sind, da Foo und Bar als Konstanten deklariert sind. TypeScript nutzt dies aus und gibt Foo und Bar einen neuen Typ: eindeutige Symbole.

Eindeutige Symbole sind Untertypen von Symbolen und können nur durch den Aufruf von Symbol() oder Symbol.for() oder durch explizite Typanmerkungen generiert werden. Sie erscheinen nur in Konstantendeklarationen und schreibgeschützten statischen Eigenschaften. Um auf einen vorhandenen eindeutigen Symboltyp zu verweisen, müssen Sie den Operator „typeof“ verwenden. Jeder Verweis auf ein eindeutiges Symbol impliziert eine völlig eindeutig deklarierte Identität.

// Funktioniert
Deklarieren Sie const Foo: eindeutiges Symbol;

// Fehler! „Bar“ ist keine Konstante.
let Bar: eindeutiges Symbol = Symbol();

// Funktioniert – bezieht sich auf ein eindeutiges Symbol, aber seine Identität ist an „Foo“ gebunden.
lass Baz: Typ von Foo = Foo;

// Funktioniert auch.
Klasse C {
    statisches schreibgeschütztes StaticSymbol: eindeutiges Symbol = Symbol();
}

Da jedes eindeutige Symbol eine völlig unabhängige Identität hat, können zwei eindeutige Symboltypen nicht zugewiesen oder verglichen werden.

const Foo = Symbol();
const Bar = Symbol();

// Fehler: Zwei eindeutige Symbole können nicht verglichen werden.
wenn (Foo === Bar) {
    // ...
}

Ein weiterer möglicher Anwendungsfall ist die Verwendung von Symbolen als Gelenkmarkierungen.

// ./ShapeKind.ts
export const Kreis = Symbol("Kreis");
export const Square = Symbol("Quadrat");

// ./ShapeFun.ts
importiere * als ShapeKind aus "./ShapeKind";

Schnittstelle Kreis {
    Art: Typ von ShapeKind.Circle;
    Radius: Zahl;
}

Schnittstelle Quadrat {
    Art: Typ von ShapeKind.Square;
    Seitenlänge: Zahl;
}

Funktion Fläche(Form: Kreis | Quadrat) {
    wenn (Form.Art === FormArt.Kreis) {
        // „Form“ hat den Typ „Kreis“
        gibt Math.PI * Form.Radius ** 2 zurück;
    }
    // „Form“ hat den Typ „Quadrat“
    gibt Form.Seitenlänge zurück ** 2;
}

Strengere Überprüfung der Klassenattribute

TypeScript 2.7 führt eine neue Compileroption zur strengen Überprüfung der Eigenschaftsinitialisierung in Klassen ein. Wenn das Flag --strictPropertyInitialization aktiviert ist, überprüft der Typprüfer, ob jede in einer Klasse deklarierte Instanzeigenschaft

  • Gibt es einen Typ, der „undefined“ enthält?
  • Es gibt einen expliziten Initialisierer
  • Definitiv im Konstruktor zugewiesen

Die Option --strictPropertyInitialization ist Teil der Familie der Compileroptionen und wird automatisch aktiviert, wenn das Flag --strict gesetzt ist. Wie bei allen anderen strengen Compileroptionen können wir --strict auf „true“ setzen und die strenge Überprüfung der Eigenschafteninitialisierung selektiv deaktivieren, indem wir --strictPropertyInitialization auf „false“ setzen.

Beachten Sie, dass das Flag --strictNullCheck gesetzt sein muss (entweder direkt oder indirekt über --strict), damit --strictPropertyInitialization eine Wirkung hat.

Sehen wir uns nun die strenge Überprüfung der Eigenschafteninitialisierung an. Ohne aktiviertes Flag --strictpropertyinitialized verläuft die Typprüfung im folgenden Code zwar einwandfrei, zur Laufzeit wird jedoch ein TypeError generiert:

Klasse Benutzer {
  Benutzername: Zeichenfolge;
}

const Benutzer = neuer Benutzer();

// TypeError: Eigenschaft „toLowerCase“ von undefined kann nicht gelesen werden
const Benutzername = Benutzer.Benutzername.toLowerCase();

Der Grund für den Laufzeitfehler liegt darin, dass der Eigenschaftswert „Benutzername“ nicht definiert ist, da der Eigenschaft kein Wert zugewiesen wurde. Daher schlägt der Aufruf der Methode toLowerCase() fehl.

Wenn Sie --strictpropertyinitialize aktivieren, meldet der Typprüfer einen Fehler:

Klasse Benutzer {
  // Typfehler: Eigenschaft „Benutzername“ hat keinen Initialisierer
  // und ist im Konstruktor nicht eindeutig zugewiesen
  Benutzername: Zeichenfolge;
}

Als Nächstes sehen wir uns vier verschiedene Möglichkeiten an, wie wir unsere Benutzerklasse richtig typisieren können, um Typfehler zu vermeiden.

Lösung 1: Definition zulassen

Eine Möglichkeit, den Typfehler zu beseitigen, besteht darin, der Eigenschaft „Benutzername“ einen Typ zuzuweisen, der „undefined“ enthält:

Klasse Benutzer {
  Benutzername: Zeichenfolge | nicht definiert;
}

const Benutzer = neuer Benutzer();

Nun ist es völlig gültig, dass die Eigenschaft „Benutzername“ den Wert „undefiniert“ enthält. Wenn wir jedoch die Eigenschaft username als Zeichenfolge verwenden möchten, müssen wir zunächst sicherstellen, dass sie tatsächlich eine Zeichenfolge und keinen undefinierten Wert enthält, beispielsweise mithilfe von typeof

// OK
const Benutzername = Typ von Benutzer.Benutzername === "Zeichenfolge"
  ? Benutzer.Benutzername.toLowerCase()
  : "n / A";

Lösung 2: Explizite Eigenschaftsinitialisierung

Eine andere Möglichkeit, den Typfehler zu beseitigen, besteht darin, der Benutzernameneigenschaft einen expliziten Initialisierer hinzuzufügen. Auf diese Weise enthält die Eigenschaft sofort einen Zeichenfolgenwert und ist nicht undefiniert:

Klasse Benutzer {
  Benutzername = "n/a";
}

const Benutzer = neuer Benutzer();

// OK
const Benutzername = Benutzer.Benutzername.toLowerCase();

Lösung 3: Konstruktorzuweisung verwenden

Die vielleicht nützlichste Lösung besteht darin, dem Konstruktor einen Benutzernamenparameter hinzuzufügen und ihn dann der Benutzernameneigenschaft zuzuweisen. Wenn also eine Instanz der Klasse User erstellt wird, muss der Anrufer den Benutzernamen als Argument angeben:

Klasse Benutzer {
  Benutzername: Zeichenfolge;

  Konstruktor(Benutzername: Zeichenfolge) {
    this.username = Benutzername;
  }
}

const user = neuer Benutzer("mariusschulz");

// OK
const Benutzername = Benutzer.Benutzername.toLowerCase();

Wir können die Benutzerklasse auch vereinfachen, indem wir die explizite Zuweisung zu den Klassenfeldern entfernen und den öffentlichen Modifikator wie folgt zum Konstruktorparameter „Benutzername“ hinzufügen:

Klasse Benutzer {
  Konstruktor (öffentlicher Benutzername: Zeichenfolge) {}
}

const user = neuer Benutzer("mariusschulz");

// OK
const Benutzername = Benutzer.Benutzername.toLowerCase();

Beachten Sie, dass bei der strikten Eigenschafteninitialisierung jede Eigenschaft in allen möglichen Codepfaden innerhalb des Konstruktors explizit zugewiesen werden muss. Daher ist der folgende Code nicht typkorrekt, da wir in einigen Fällen die Eigenschaft „Benutzername“ einem nicht initialisierten Status zuweisen:

Klasse Benutzer {
  // Typfehler: Eigenschaft „Benutzername“ hat keinen Initialisierer
  // und ist im Konstruktor nicht definitiv zugewiesen.
  Benutzername: Zeichenfolge;

  Konstruktor(Benutzername: Zeichenfolge) {
    wenn (Math.random() < 0,5) {
      this.username = Benutzername;
    }
  }
}

Lösung 4: Explizite Zuweisungsbehauptungen

Wenn eine Klasseneigenschaft weder explizit initialisiert ist noch den Typ undefined hat, erfordert der Typprüfer, dass die Eigenschaft direkt im Konstruktor initialisiert wird. Andernfalls schlägt die strenge Eigenschaftsinitialisierungsprüfung fehl. Dies ist problematisch, wenn wir Eigenschaften in Hilfsmethoden initialisieren möchten oder die Eigenschaften von einem Dependency-Injection-Framework für uns initialisieren lassen möchten. In diesen Fällen müssen wir der Deklaration der Eigenschaft eine explizite Zuweisungsbehauptung (!) hinzufügen:

Klasse Benutzer {
  Benutzername!: Zeichenfolge;

  Konstruktor(Benutzername: Zeichenfolge) {
    this.initialize(Benutzername);
  }

  private initialisieren(Benutzername: Zeichenfolge) {
    this.username = Benutzername;
  }
}

const user = neuer Benutzer("mariusschulz");

// OK
const Benutzername = Benutzer.Benutzername.toLowerCase();

Durch das Hinzufügen einer eindeutigen Zuweisungsbehauptung zur Eigenschaft „Benutzername“ wird dem Typprüfer mitgeteilt, dass er eine Initialisierung der Eigenschaft „Benutzername“ erwartet, auch wenn er dies nicht selbst erkennen kann. Jetzt liegt es in unserer Verantwortung, sicherzustellen, dass wir ihm die Eigenschaft nach der Rückkehr des Konstruktors explizit zuweisen. Daher müssen wir vorsichtig sein. Andernfalls ist die Benutzername-Eigenschaft möglicherweise explizit nicht definiert oder es wird zur Laufzeit ein TypeError ausgelöst.

Explizite Zuweisungsbehauptungen

Obwohl wir versuchen, das Typsystem so ausdrucksstark wie möglich zu gestalten, wissen wir, dass Benutzer Typen manchmal besser verstehen als TypeScript.

Wie oben erwähnt, handelt es sich bei expliziten Zuweisungsbehauptungen um eine neue Syntax, mit der Sie TypeScript mitteilen, dass einer Eigenschaft explizit ein Wert zugewiesen wird. Aber zusätzlich zur Verwendung auf Klasseneigenschaften können Sie es mit TypeScript 2.7 auch auf Variablendeklarationen verwenden!

lass x!: Zahl[];
initialisieren();
x.push(4);

Funktion initialisieren() {
    x = [0, 1, 2, 3];
}

Wenn wir nach x kein Ausrufezeichen gesetzt hätten, hätte TypeScript gemeldet, dass x nie initialisiert worden sei. Die Verwendung ist praktisch in Szenarien, in denen eine verzögerte Initialisierung oder Neuinitialisierung erforderlich ist.

Oben finden Sie eine ausführliche Erläuterung der numerischen Trennzeichen von TS und strengeren Klassenattributprüfungen. Weitere Informationen zu TS finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Standardtypen für generische TypeScript-Parameter und neue Option zur strikten Kompilierung
  • Detaillierte Erklärung der privaten Klassenfelder von JavaScript und der privaten Modifizierungen von TypeScript
  • Detaillierte Erklärung des Typschutzes in TypeScript
  • Eine kurze Diskussion über den Typschutzmechanismus von TypeScript
  • Detaillierte Erklärung zum Schreiben von TypeScript-Typdeklarationen
  • Grundlegende TypeScript-Datentypen
  • TypeScript lernt erzwungene Typkonvertierung
  • TypeScript-Typinferenz

<<:  Zusammenfassung der allgemeinen Bedienungskenntnisse der MySQL-Datenbank

>>:  So stellen Sie Dienste in Windows Server 2016 bereit (Grafisches Tutorial)

Artikel empfehlen

So benennen Sie unter Linux eine Gruppe von Dateien auf einmal um

Unter Linux verwenden wir normalerweise den Befeh...

So konfigurieren Sie SSL für den Koa2-Dienst

I. Einleitung 1: SSL-Zertifikat Mein Domänenname ...

MySQL bedingte Abfrage und/oder Verwendung und Priorität Beispielanalyse

Dieser Artikel veranschaulicht anhand von Beispie...

Tutorial zur Verwendung des iostat-Befehls unter Linux

Vorwort Es wird gesagt, dass sich die für Betrieb...

Lösen Sie das Problem inkonsistenter Front- und Back-End-Ports von Vue

Die Front- und Back-End-Ports von Vue sind inkons...

Tutorial zur Verwendung des Hyperlink-Tags in HTML

Die verschiedenen HTML-Dokumente der Website sind...

Beispiele für die Verwendung des Li-Tags in HTML

Ich möchte den Titel links und das Datum rechts a...

Tipps zum Organisieren von Zeichenfolgen in Linux

Bei Linux-Operationen ersetzen und zählen wir häu...

HTML-Zeichnungsbenutzer-Registrierungsseite

In diesem Artikel wird der spezifische Implementi...

Der gesamte Prozessdatensatz der rekursiven Komponentenkapselung von Vue3

Inhaltsverzeichnis Vorwort 1. Rekursive Komponent...

Beispiel für die Implementierung der Hochverfügbarkeit von Keepalived+Nginx

1. Einführung in Keepalived Keepalived wurde ursp...

Vues Leitfaden zu Fallstricken bei der Verwendung von Drosselungsfunktionen

Vorwort In einem üblichen Geschäftsszenario müsse...

Detaillierte Analyse des langsamen Abfrageproblems beim Senden von MySQL-Daten

Anhand eines Beispiels habe ich Ihnen die Lösung ...