Utility Types sind in TypeScript eingebaute generische Typen, die einen Typ in einen anderen verwandeln. Statt einen zweiten User-Typ mit lauter optionalen Feldern zu schreiben, schreibst du Partial<User>; statt drei Felder zu kopieren, Pick<User, "id" | "name">. Sie sind global, ein Import ist also nicht nötig.
Wenn sich User ändert, ziehen alle vier abgeleiteten Typen mit. Die Standardbibliothek von TypeScript (lib.es5.d.ts) deklariert 22 Utility Types. Die folgenden Abschnitte listen alle auf, gruppiert nach der Art von Typ, mit der sie arbeiten, und mit einem Link zur ausführlichen Seite, wo es eine gibt.
Objekttypen: Partial, Required, Readonly, Pick, Omit, Record
| Utility Type | Was er macht | Beispiel |
|---|---|---|
Partial<T> | macht jede Eigenschaft optional | Partial<User> für die Daten eines Updates |
Required<T> | macht jede Eigenschaft verpflichtend (entfernt ?) | Required<Config>, nachdem Standardwerte angewendet wurden |
Readonly<T> | macht jede Eigenschaft readonly | Readonly<State> |
Pick<T, K> | behält nur die Schlüssel K | Pick<User, "id" | "name"> |
Omit<T, K> | entfernt die Schlüssel K | Omit<User, "password"> |
Record<K, V> | ein Objekttyp mit Schlüsseln K und Werten V | Record<"en" | "de", string> |
Required<T> ist das Gegenteil von Partial<T>; die Partial-Seite behandelt beide, auch das explizite undefined, das ein Spread wie dieser durchlässt.
Union-Typen: Exclude, Extract, NonNullable
| Utility Type | Was er macht | Beispiel |
|---|---|---|
Exclude<U, M> | entfernt Union-Mitglieder, die an M zuweisbar sind | Exclude<"a" | "b" | "c", "a"> ist "b" | "c" |
Extract<U, M> | behält Union-Mitglieder, die an M zuweisbar sind | Extract<string | number, number> ist number |
NonNullable<T> | entfernt null und undefined | NonNullable<string | null> ist string |
Diese drei arbeiten mit Unions, nicht mit Objekten. Das ist der entscheidende Unterschied zu Pick und Omit, die einen Objekttyp und eine Liste seiner Schlüssel nehmen.
Funktions- und Klassentypen: Parameters, ReturnType und mehr
| Utility Type | Was er macht | Beispiel |
|---|---|---|
ReturnType<F> | der Rückgabetyp eines Funktionstyps | ReturnType<typeof createStore> |
Parameters<F> | die Parametertypen als Tupel | Parameters<typeof fetchPage>[0] |
ConstructorParameters<C> | die Parameter eines Klassenkonstruktors als Tupel | ConstructorParameters<typeof Point> |
InstanceType<C> | der Instanztyp, den ein Konstruktor erzeugt | InstanceType<typeof Point> |
ThisParameterType<F> | der Typ des this-Parameters einer Funktion | ThisParameterType<typeof greet> |
OmitThisParameter<F> | der Funktionstyp ohne seinen this-Parameter | der Typ von greet.bind(obj) |
ThisType<T> | legt den Typ von this in den Methoden eines Objektliterals fest | mit noImplicitThis in Builder-APIs |
NoInfer<T> | verhindert, dass ein Typparameter aus dieser Position abgeleitet wird | fallback: NoInfer<C> |
typeof createOrder ist nötig, weil diese Utilities einen Typ nehmen und createOrder ein Wert ist. Dasselbe gilt für Klassen: typeof Point ist der Konstruktortyp, während ein einfaches Point als Typ bereits den Instanztyp bedeutet.
NoInfer steuert, woher ein Generic seinen Typ bekommt:
Ohne NoInfer würde TypeScript C aus beiden Argumenten ableiten und auf "red" | "green" | "blue" erweitern, also würde der Tippfehler im Fallback akzeptiert.
String-Typen: Uppercase, Lowercase, Capitalize, Uncapitalize
| Utility Type | Was er macht | Beispiel |
|---|---|---|
Uppercase<S> | schreibt einen String-Literaltyp groß | Uppercase<"get"> ist "GET" |
Lowercase<S> | schreibt ihn klein | Lowercase<"GET"> ist "get" |
Capitalize<S> | schreibt das erste Zeichen groß | Capitalize<"name"> ist "Name" |
Uncapitalize<S> | schreibt das erste Zeichen klein | Uncapitalize<"Name"> ist "name" |
Diese vier sind im Compiler eingebaut statt in TypeScript geschrieben, und am nützlichsten sind sie in Template Literal Types, etwa `on${Capitalize<E>}` für Namen von Event-Handlern.
Promises: Awaited
| Utility Type | Was er macht | Beispiel |
|---|---|---|
Awaited<T> | der Typ, den du von await bekommst, verschachtelte Promises ausgepackt | Awaited<Promise<Promise<number>>> ist number |
Awaited<ReturnType<typeof fn>> ist der Standardweg, den Ergebnistyp einer async-Funktion zu benennen, ohne ihn separat zu deklarieren.
Utility Types kombinieren
Utility Types lassen sich verschachteln. Einige Kombinationen kommen oft genug vor, um sie auswendig zu kennen:
Lies einen verschachtelten Utility Type von innen nach außen: Readonly<Pick<Post, "id" | "title">> behält zuerst zwei Eigenschaften und macht sie dann schreibgeschützt. Aus denselben Teilen entsteht ein Helfer PartialBy, der nur einige Schlüssel optional macht; die Partial-Seite schreibt ihn aus.
Utility Types bewirken zur Laufzeit nichts
Jeder Utility Type wird beim Kompilieren entfernt. Ein Wert vom Typ Omit<User, "password"> kann zur Laufzeit trotzdem ein Passwort tragen, wenn das Objekt, aus dem er stammt, eines hatte:
Der Typ schränkt nur ein, was dein Code lesen darf. Um ein Feld aus den Daten zu entfernen, entferne es per Destructuring wie in den letzten Zeilen, und um Änderungen zur Laufzeit zu verhindern, nimm Object.freeze, nicht Readonly. Die eingebauten Typen sind einzeilige Mapped Types und Conditional Types, also kannst du mit denselben Werkzeugen eigene schreiben.
Häufig gestellte Fragen
Was sind Utility Types in TypeScript?
Generische Typen, die mit TypeScript mitkommen und andere Typen umwandeln: Partial<T> macht jede Eigenschaft optional, Pick<T, K> behält einige Eigenschaften, ReturnType<F> liefert den Rückgabetyp einer Funktion und so weiter. Sie sind in der Standardbibliothek deklariert, du nutzt sie also ohne Import.
Muss ich Utility Types importieren?
Nein. Partial, Omit, Record, ReturnType und der Rest sind globale Typen aus den eingebauten Bibliotheksdateien von TypeScript. Schreibe Partial<User> an beliebiger Stelle; weder ein Import noch ein npm-Paket ist nötig.
Welche Utility Types sind in TypeScript eingebaut?
22, alle in lib.es5.d.ts deklariert: Partial, Required, Readonly, Pick, Omit, Record, Exclude, Extract, NonNullable, Parameters, ConstructorParameters, ReturnType, InstanceType, ThisParameterType, OmitThisParameter, ThisType, NoInfer, Awaited, Uppercase, Lowercase, Capitalize und Uncapitalize.
Ändern Utility Types Objekte zur Laufzeit?
Nein. Sie beschreiben nur Typen und werden aus der JavaScript-Ausgabe entfernt. Omit<User, "password"> löscht keine Eigenschaft password, und Readonly<T> friert nichts ein. Um das eigentliche Objekt zu ändern, schreibst du den Code: ein Rest-Pattern beim Destructuring, Object.freeze und so weiter.
Kann ich eigene Utility Types schreiben?
Ja. Die eingebauten sind gewöhnliches TypeScript: Die meisten sind einzeilige Mapped Types oder Conditional Types in lib.es5.d.ts. type Nullable<T> = { [K in keyof T]: T[K] | null } ist ein eigener Utility Type, auf dieselbe Weise geschrieben.