Menu

TypeScript Omit: Eigenschaften aus einem Typ entfernen

Omit<T, K> erzeugt einen Typ mit allen Eigenschaften von T außer den Schlüsseln K. Omit mit einem und mehreren Schlüsseln, einen Eigenschaftstyp überschreiben, die Eigenschaft zur Laufzeit entfernen, Omit vs Exclude und Pick, warum Omit Schlüssel akzeptiert, die es nicht gibt, ein striktes Omit und Omit auf Union-Typen.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Omit<T, K> ist ein eingebauter Utility Type, der einen Objekttyp mit allen Eigenschaften von T erzeugt, außer den in K aufgeführten Schlüsseln. Der klassische Einsatz: ein Typ User mit einem password und eine öffentliche Version ohne.

PublicUser bleibt mit User synchron: Füge User ein Feld hinzu, und es erscheint auch in PublicUser, während password draußen bleibt. Omit arbeitet nur mit Typen; das Destructuring in toPublic ist das, was die Eigenschaft aus dem eigentlichen Objekt entfernt.

Mehrere Schlüssel weglassen

Das zweite Argument ist eine Union, also listest du mit | so viele Schlüssel auf, wie du brauchst:

"Der Typ ohne die Felder, die der Server erzeugt" ist das häufigste Omit in echtem Code: ein Modelltyp, aus dem Omit die Eingabeform für Erstellungsformulare und API-Requests ableitet.

Wie Omit definiert ist

Omit ist aus zwei anderen Utility Types gebaut:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Lies es von innen: keyof T ist die Union aller Schlüssel, Exclude<keyof T, K> entfernt die unerwünschten aus dieser Union, und Pick behält die übrigen Schlüssel. Also ist Omit<User, "password"> gleich Pick<User, "id" | "name" | "email">. Weil Pick über die Schlüssel von T iteriert, bleiben die Modifikatoren readonly und ? bei den übrigen Eigenschaften erhalten.

Beachte den Constraint: K extends keyof any, nicht K extends keyof T. Um dieses Detail geht es im nächsten Abschnitt.

Omit prüft nicht, ob der Schlüssel existiert

keyof any ist string | number | symbol, also akzeptiert Omit jeden beliebigen Schlüssel, auch einen, den T nicht hat. Ein Tippfehler kompiliert und entfernt nichts:

Nirgends ein Fehler, und das Passwort landet im JSON. Der lockere Constraint war eine bewusste Entscheidung, als Omit in TypeScript 3.5 hinzukam. Die Gründe des Teams: In generischem Code stammen die weggelassenen Schlüssel oft nicht aus T (eine Funktion, die zwei Objekte zusammenführt, kann ihr Rest-Argument als Omit<T1, keyof T2> typisieren), Exclude beschränkt seinen zweiten Parameter ebenfalls nicht, und eine beschränkte Version hätte viele der selbst geschriebenen Omit-Typen gebrochen, die Projekte schon hatten. Für alltäglichen Code fängt eine striktere Version den Tippfehler ab:

index.ts(10,36): error TS2344: Type '"pasword"' does not satisfy the constraint 'keyof User'.
index.ts(11,7): error TS2741: Property 'password' is missing in type '{ id: number; name: string; }' but required in type 'PublicUser'.

Der erste Fehler ist der entscheidende: Der Schlüssel wird gegen keyof User geprüft. Der zweite folgt daraus, weil nichts weggelassen wurde und PublicUser weiterhin password verlangt. Korrigiere die Schreibweise zu "password", und es kompiliert. Viele Codebasen definieren das einmal in einer gemeinsamen Typdatei und verwenden es statt Omit.

Einen Eigenschaftstyp überschreiben

Ein Interface kann kein anderes erweitern und dabei eine Eigenschaft auf einen inkompatiblen Typ ändern: interface ApiUser extends User { id: string } scheitert mit error TS2430: Interface 'ApiUser' incorrectly extends interface 'User'. Lass die Eigenschaft zuerst weg und deklariere sie dann neu:

Mit einem Type Alias ist dasselbe eine Intersection: type ApiUser = Omit<User, "id"> & { id: string }. Ohne das Omit gäbe die Intersection User & { id: string } der Eigenschaft id den Typ number & string, und das ist never.

Omit auf Union-Typen

Omit ist nicht distributiv. Auf einer Union beginnt es mit keyof der ganzen Union, und das enthält nur die Schlüssel, die alle Mitglieder gemeinsam haben, also verschwinden die eigenen Eigenschaften der Mitglieder:

index.ts(8,39): error TS2353: Object literal may only specify known properties, and 'radius' does not exist in type 'NewShape'.

NewShape wurde zu { kind: "circle" | "square" }. Um aus jedem Mitglied einzeln wegzulassen, wende Omit innerhalb eines distributiven Conditional Types an:

T extends unknown ? ... : never ist immer wahr; es steht nur da, damit sich der Conditional Type über die Union verteilt. Die Seite zu Conditional Types erklärt, warum das funktioniert. Nimm DistributiveOmit, wann immer der Typ, aus dem du weglässt, eine Discriminated Union ist.

Eine verschachtelte Eigenschaft weglassen

Omit betrachtet nur die Schlüssel der obersten Ebene, also entfernt Omit<Settings, "editor.wordWrap"> nichts (und beschwert sich, wie oben gezeigt, auch nicht). Um eine Eigenschaft eine Ebene tiefer zu entfernen, lass das Elternobjekt weg und füge es mit einem eigenen Omit wieder hinzu:

Settings["editor"] ist ein Indexed Access Type: Er benennt den verschachtelten Objekttyp, damit Omit damit arbeiten kann.

Eigenschaften zur Laufzeit entfernen

Omit ändert nur den Typ. Um ein Objekt ohne die Eigenschaften zu erzeugen, entferne sie per Destructuring oder schreibe einen kleinen generischen Helfer, dessen Rückgabetyp Omit ist:

Der Helfer beschränkt K auf keyof T, also ist ein falsch geschriebener Schlüssel an der Aufrufstelle ein Fehler. Die Kopie ist flach: Verschachtelte Objekte teilt sie mit dem Original.

Omit vs Pick vs Exclude

Arbeitet mitBehältBeispielErgebnis
Omit<T, K>Objekttypenjede Eigenschaft außer KOmit<User, "password">{ id; name; email }
Pick<T, K>Objekttypennur die Eigenschaften in KPick<User, "id" | "name">{ id; name }
Exclude<U, M>Union-TypenUnion-Mitglieder, die nicht an M zuweisbar sindExclude<"a" | "b" | "c", "a">"b" | "c"

Wähle zwischen Omit und Pick danach, was passieren soll, wenn der Quelltyp wächst. Omit nimmt neue Eigenschaften automatisch auf, und das passt zu "alles außer dem Geheimnis". Pick nimmt sie nie auf, und das passt zu "genau diese Felder" und ist sicherer für alles, was deinen Server verlässt. Pick prüft seine Schlüssel gegen keyof T; Omit tut das nicht.

Häufig gestellte Fragen

Was macht Omit in TypeScript?

Omit<T, K> baut einen neuen Objekttyp mit allen Eigenschaften von T außer denen, deren Schlüssel in K stehen. Omit<User, "password"> ist User ohne password. Es ändert nur den Typ; zur Laufzeit entfernt es nichts aus einem Objekt.

Wie lasse ich in TypeScript mehrere Eigenschaften weg?

Übergib als zweites Argument eine Union von Schlüsseln: Omit<User, "password" | "createdAt" | "updatedAt">. Jeder Schlüssel in der Union wird entfernt.

Was ist der Unterschied zwischen Omit und Exclude?

Omit entfernt Eigenschaften aus einem Objekttyp: Omit<{ a: 1; b: 2 }, "a"> ist { b: 2 }. Exclude entfernt Mitglieder aus einem Union-Typ: Exclude<"a" | "b", "a"> ist "b". Omit ist mit Exclude implementiert: Pick<T, Exclude<keyof T, K>>.

Warum meldet Omit keinen Fehler bei einem Schlüssel, den es nicht gibt?

Sein Schlüsselparameter ist auf keyof any beschränkt (jeder String, jede Zahl, jedes Symbol), nicht auf keyof T, also kompiliert ein Tippfehler wie Omit<User, "pasword"> und entfernt nichts. Definiere type StrictOmit<T, K extends keyof T> = Omit<T, K>, um bei unbekannten Schlüsseln einen Fehler zu bekommen.

Wie entferne ich in TypeScript eine Eigenschaft zur Laufzeit aus einem Objekt?

Mit Destructuring und einem Rest-Element: const { password, ...rest } = user;. TypeScript typisiert rest als das Objekt ohne password, dieselbe Form wie Omit<User, "password">, und die Eigenschaft ist im neuen Objekt wirklich weg.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S