ProgrammierungFullstack-Entwickler

Wie funktioniert Exclude in TypeScript, wann es für Manipulationen mit Union-Typen verwendet werden kann und welche Nuancen es bei der Arbeit mit diesem Utility-Typ gibt?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Exclude<T, U> ist ein Utility-Typ, der in TypeScript eingeführt wurde, um einen Typ von einem anderen abzuziehen, wenn bestimmte Werte aus einem Union-Typ ausgeschlossen werden müssen.

Vorgeschichte

Ursprünglich gab es in TypeScript keinen bequemen Weg, einen Typ von einem anderen abzuziehen. Bei der Erstellung von generischen APIs und auch beim Refactoring war es häufig erforderlich, einen "Rest-Typ" zu erhalten – alles außer den verbotenen Werten. Anstatt manuelle Manipulationen mit Union durchzuführen, mussten mehrere ähnliche Interfaces unterstützt werden.

Problem

Zum Beispiel, wenn es einen Typ 'A | B | C' gibt, aber man den Typ ohne B erhalten möchte. Dies ist häufig erforderlich, wenn komplexe Eingabeparameter für Funktionen erstellt, erlaubte Werte gefiltert und dynamisch Typen gebildet werden.

Lösung

Exclude löst dieses Problem. Seine vereinfachte Signatur lautet:

type Exclude<T, U> = T extends U ? never : T;

Er gibt einen Typ zurück, der alle Mitglieder von U aus T ausschließt.

Beispiel:

type Status = 'draft' | 'published' | 'removed'; type UserVisibleStatus = Exclude<Status, 'removed'>; const visible: UserVisibleStatus = 'draft'; // OK

Wichtige Merkmale:

  • Ermöglicht die Bildung dynamischer Typen durch "Abzug" von Teilen aus der Union.
  • Vereinfacht das Refactoring – bei Änderungen des Basistyps werden alle abgeleiteten Typen automatisch aktualisiert.
  • Kann beispielsweise zur Filterung von switch-Fällen oder Objekt-Schlüsseln verwendet werden.

Fangfragen.

Kann man Exclude auch für einfache Typen und nicht für Union verwenden?

Wenn T kein Union-Typ ist, aber in U enthalten ist – funktioniert Exclude trotzdem, aber das Ergebnis könnte never oder T sein, was nicht immer intuitiv ist.

Exclude<'a', 'a'> // Ergebnis: never Exclude<'a', 'b'> // Ergebnis: 'a'

Entfernt Exclude alle Erwähnungen des Typs in der Objektstruktur?

Nein, Exclude geht nicht rekursiv durch verschachtelte Felder des Typs und schließt nur auf der obersten Ebene der Union aus.

Wie funktioniert Exclude mit Interfaces und Objekt-Typen?

Er vergleicht den gesamten Typ, nicht einzelne Eigenschaften. Daher entfernt Exclude aus einer Union mehrerer Interfaces nur die, die vollständig mit U übereinstimmen.

interface A { x: number }; interface B { y: string }; // Exclude<A|B, B> ergibt: A (B stimmt vollständig überein)

Typische Fehler und Anti-Patterns

  • Versuche, Exclude für verschachtelte oder partielle Übereinstimmungen zu verwenden
  • Verwendung zum "Löschen" von Eigenschaften eines Interfaces und nicht von Union-Optionen
  • Ignorieren der Möglichkeit, den Typ never bei vollständiger Übereinstimmung der Typen zu erhalten

Beispiel aus dem Leben

Negativer Fall

Validierung der Benutzerrollen durch Exclude<UserRoles, 'admin'>, aber vergessen, dass Exclude nicht auf verschachtelte Strukturen angewendet wird – die Berechtigungen 'admin:sub' wurden nicht ausgeschlossen.

Vorteile:

  • Einfachheit bei der Bildung des Rollentyps.

Nachteile:

  • Unklare Verhaltensweise mit verschachtelten oder ähnlichen Typen; eine kritische Rolle wurde übersehen.

Positiver Fall

Verwendung von Exclude zur Einschränkung der öffentlichen API durch die Aktionen: Exclude<Action, 'delete'>, was die gefährliche Operation ausschließt.

Vorteile:

  • Sicherheit auf Typisierungsebene, es kann keine verbotene Aktion aufgerufen werden.

Nachteile:

  • Aktuelle Listen von Basistypen müssen gepflegt werden.