Mapped Types in TypeScript
Mapped Types
~14 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Kapitel 20 hat gezeigt, WIE man Partial/Readonly BENUTZT – dieses Kapitel zeigt, WIE sie tatsächlich INTERN funktionieren, und wie man EIGENE, projektspezifische Utility Types nach demselben Muster baut.
Die Grundsyntax eines Mapped Type
type MeinPartial<T> = {
[K in keyof T]?: T[K];
};
// Angewendet auf Buch, ergibt das GENAU dasselbe wie das eingebaute Partial<Buch>:
// { isbn?: string; titel?: string; autor?: Autor; ... }[K in keyof T] ITERIERT über JEDEN Eigenschaftsnamen von T (keyof T aus Kapitel 18) – für JEDEN dieser Schlüssel K wird eine neue Eigenschaft mit demselben Namen erzeugt, deren Typ T[K] ist (der URSPRÜNGLICHE Typ dieser Eigenschaft). Das ? macht sie zusätzlich optional – GENAU die Definition von Partial.
Modifizierer hinzufügen UND entfernen
// readonly HINZUFÜGEN (wie das eingebaute Readonly<T>):
type MeinReadonly<T> = {
readonly [K in keyof T]: T[K];
};
// readonly ENTFERNEN mit dem Minus-Präfix:
type Beschreibbar<T> = {
-readonly [K in keyof T]: T[K];
};
// optional (?) ENTFERNEN - GENAU das eingebaute Required<T>:
type MeinRequired<T> = {
[K in keyof T]-?: T[K];
};Das --Präfix vor readonly/? ist eine EXPLIZITE ENTFERNUNGS-Anweisung – nützlich, wenn Sie einen bereits readonly- oder optionalen Ausgangstyp WIEDER "aufweichen" wollen, was mit den einfachen Utility Types aus Kapitel 20 nicht direkt möglich ist.
Praxis: ein Änderungsprotokoll für Buch-Updates
Wir bauen einen eigenen Mapped Type, der zu JEDER Eigenschaft von Buch protokolliert, ob sie sich geändert hat – ein Anwendungsfall, den KEIN eingebauter Utility Type direkt abdeckt:
export type Aenderungsprotokoll<T> = {
[K in keyof T]: boolean;
};
export function erstelleAenderungsprotokoll<T extends object>(
alt: T,
neu: T,
): Aenderungsprotokoll<T> {
const protokoll = {} as Aenderungsprotokoll<T>;
for (const schluessel in alt) {
protokoll[schluessel] = alt[schluessel] !== neu[schluessel];
}
return protokoll;
}const protokoll = erstelleAenderungsprotokoll(altesBuch, neuesBuch);
// protokoll: { isbn: boolean; titel: boolean; autor: boolean; ... }
if (protokoll.titel) {
console.log('Der Titel hat sich geändert!');
}Aenderungsprotokoll<Buch> hat AUTOMATISCH dieselben Eigenschaftsnamen wie Buch, aber JEDE davon ist ein boolean statt des URSPRÜNGLICHEN Typs – kommt Buch später um eine Eigenschaft hinzu, passt sich Aenderungsprotokoll<Buch> AUTOMATISCH mit an, ohne dass Sie diesen Mapped Type manuell nachpflegen müssten.
Bonus: Schlüssel umbenennen mit as
type MitGetterPraefix<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
// Angewendet auf { titel: string; seitenzahl: number }:
// { getTitel: () => string; getSeitenzahl: () => number }Das as-Schlüsselwort INNERHALB eines Mapped Type erlaubt "Key Remapping" – hier kombiniert mit Capitalize (einem WEITEREN eingebauten Utility Type für Strings) und Template Literal Types (nächstes-übernächstes Kapitel), um AUS jedem Eigenschaftsnamen einen passenden Getter-Methodennamen zu erzeugen. Ein fortgeschrittenes Muster, hier als Vorgriff auf Kapitel 23 gezeigt.
Tipp: Faustregel: Mapped Types SELBST schreiben lohnt sich, sobald Sie ein WIEDERKEHRENDES Transformationsmuster über MEHRERE Typen hinweg brauchen (wie unser Aenderungsprotokoll), das kein eingebauter Utility Type abdeckt. Für EINMALIGE, spezifische Anpassungen reicht oft ein simples, manuell geschriebenes Interface.