Objektliterale präzise prüfen, ohne Literal-Informationen zu verlieren
Der mit TypeScript 4.9 eingeführte satisfies Operator schließt eine Lücke zwischen expliziten Typannotationen und as Type Assertions. Er prüft, ob ein Ausdruck zu einem Typ passt, behält dabei aber den konkreten, engeren Typ des Ausdrucks bei.
Inhaltsverzeichnis
- 1. Das Problem: Annotation oder Assertion
- 2. Grundlagen der Syntax
- 3. Kombination mit as const
- 4. Verwendung mit Record und Union-Schlüsseln
- 5. Konfigurationsobjekte und Funktionsrückgaben
- 6. Verschachtelte Strukturen und Arrays
- 7. Vergleich zu as und expliziten Annotationen
- 8. Best Practices für den Einsatz
- 9. Fallstricke im Alltag
- 10. Zusammenfassung
- 11. FAQ
1. Das Problem: Annotation oder Assertion
Vor TypeScript 4.9 gab es für die Prüfung eines Objektliterals gegen einen Typ nur zwei Werkzeuge, und beide hatten Nachteile. Eine explizite Typannotation wie const config: Config = { ... } prüft die Struktur zuverlässig, weitet aber jede Eigenschaft auf den deklarierten Typ auf. Ein Feld vom Typ string bleibt string, selbst wenn der tatsächliche Wert nur "json" oder "xml" sein kann.
Die Alternative war eine Type Assertion mit as. Diese behält zwar die engeren Literal-Typen bei, verzichtet aber komplett auf eine strukturelle Prüfung durch den Compiler. Tippfehler in Eigenschaftsnamen oder fehlende Pflichtfelder fallen so erst zur Laufzeit auf, wenn überhaupt.
Der satisfies Operator kombiniert beide Eigenschaften: Er validiert den Ausdruck gegen einen Typ und meldet Fehler bei fehlenden oder falschen Feldern, behält aber gleichzeitig den konkreten, abgeleiteten Typ des Ausdrucks bei, statt ihn auf den geprüften Typ zu weiten.
2. Grundlagen der Syntax
Die Syntax ist bewusst schlicht gehalten: Ein Ausdruck wird gefolgt vom Schlüsselwort satisfies und einem Typ. Der Compiler prüft die Kompatibilität, der resultierende Typ der Variable bleibt aber der ursprüngliche, engere Typ des Ausdrucks.
Im folgenden Beispiel wird ein Konfigurationsobjekt gegen ein Interface geprüft. Trotz der Prüfung bleibt der Typ von route das Literal "GET" und nicht der breitere Typ string.
Diese Reihenfolge, Ausdruck zuerst und Typ danach, unterscheidet satisfies bewusst von einer Typannotation, bei der der Typ vor dem Ausdruck steht. Der Compiler liest den Operator dadurch als nachträgliche Prüfung, nicht als vorab festgelegte Deklaration, was sich auf die Art der Typinferenz auswirkt.
interface RouteConfig {
method: string;
path: string;
cache: boolean;
}
const route = {
method: "GET",
path: "/products",
cache: true,
} satisfies RouteConfig;
// route.method hat den Typ "GET", nicht string
function handle(method: "GET" | "POST") {
// ...
}
handle(route.method); // funktioniert, weil "GET" literal erhalten bleibt
3. Kombination mit as const
Besonders nützlich wird satisfies in Kombination mit as const. Während as const alle Eigenschaften auf ihre engstmöglichen, unveränderlichen Literal-Typen fixiert, sorgt satisfies zusätzlich für eine strukturelle Prüfung gegen ein erwartetes Interface.
Ohne satisfies müsste man bei einer reinen as const Deklaration auf eigene Faust sicherstellen, dass die Struktur zum erwarteten Schema passt. Mit satisfies bekommt man beides gleichzeitig: Validierung und maximale Typpräzision.
type Theme = {
primary: string;
secondary: string;
radius: number;
};
const theme = {
primary: "#0f172a",
secondary: "#e11d48",
radius: 8,
} as const satisfies Theme;
// theme.radius ist vom Typ 8, nicht number
// theme.primary ist vom Typ "#0f172a", nicht string
4. Verwendung mit Record und Union-Schlüsseln
Ein häufiger Anwendungsfall ist die Prüfung von Objekten mit einem festen Satz an Schlüsseln, etwa einer Zuordnung von Feature-Flags oder Routen. Mit einem Record-Typ als Prüfziel stellt satisfies sicher, dass alle erwarteten Schlüssel vorhanden sind, ohne die konkreten Werttypen zu verallgemeinern.
Das ist besonders wertvoll, wenn später mit den einzelnen Werten weitergearbeitet wird, etwa in einer switch-Anweisung, die auf konkrete Literale prüft.
Ohne satisfies müsste man entweder auf eine reine Typannotation zurückgreifen, die jeden Wert auf boolean verallgemeinert, oder eine as Assertion verwenden, die fehlende Schlüssel unbemerkt lässt. Erst die Kombination aus Record-Typ und satisfies liefert beides zugleich: vollständige Schlüsselprüfung und erhaltene Literal-Präzision der einzelnen Werte.
type Feature = "checkout" | "wishlist" | "reviews";
const featureFlags = {
checkout: true,
wishlist: false,
reviews: true,
} satisfies Record<Feature, boolean>;
// Fehlt ein Schlüssel oder ist ein zusätzlicher vorhanden,
// meldet der Compiler sofort einen Fehler.
5. Konfigurationsobjekte und Funktionsrückgaben
In Anwendungen mit vielen Konfigurationsobjekten, etwa Routendefinitionen, API-Client-Optionen oder Theme-Einstellungen, zahlt sich satisfies besonders aus. Statt jedes Objekt mit einer eigenen Typannotation zu versehen, die Literal-Informationen verwirft, bleibt die volle Präzision erhalten, während Tippfehler weiterhin auffallen.
Auch bei Funktionen, die Konfigurationsobjekte zurückgeben, lässt sich satisfies direkt auf den Rückgabewert anwenden, sodass Aufrufer von den engeren Typen profitieren.
interface ApiOptions {
baseUrl: string;
timeout: number;
retries: number;
}
function buildOptions() {
return {
baseUrl: "https://mironsoft.de/api",
timeout: 5000,
retries: 3,
} satisfies ApiOptions;
}
const options = buildOptions();
// options.timeout ist vom Typ 5000
6. Verschachtelte Strukturen und Arrays
satisfies arbeitet nicht nur mit flachen Objekten, sondern auch mit verschachtelten Strukturen und Arrays von Objekten. Der Compiler prüft rekursiv jede Ebene gegen den angegebenen Typ, während die konkreten Literal-Typen jeder Ebene erhalten bleiben.
Das erlaubt zum Beispiel Arrays von Navigationseinträgen, bei denen jeder Eintrag weiterhin seinen exakten Pfad-String als Typ behält, was in Kombination mit Routing-Bibliotheken für zusätzliche Typsicherheit sorgt.
interface NavItem {
label: string;
href: string;
external?: boolean;
}
const navigation = [
{ label: "Magento", href: "/magento" },
{ label: "Hyva", href: "/hyva" },
{ label: "Blog", href: "https://mironsoft.de/blog", external: true },
] satisfies NavItem[];
7. Vergleich zu as und expliziten Annotationen
Der entscheidende Unterschied zu as liegt in der Fehlerprüfung. Eine as Assertion sagt dem Compiler im Grunde, ihm zu vertrauen, ohne eine echte strukturelle Prüfung durchzuführen. Fehlt eine Pflichteigenschaft, meldet as keinen Fehler, satisfies dagegen schon.
Der Unterschied zur expliziten Typannotation liegt im resultierenden Typ. Eine Annotation wie const x: Config = {...} weitet die Eigenschaften auf die im Interface deklarierten Typen. satisfies dagegen leitet den Typ weiterhin vom Ausdruck selbst ab und nutzt das angegebene Interface nur zur Validierung.
In der Praxis bedeutet das: Wer sich für as entscheidet, verzichtet bewusst auf Sicherheit zugunsten von Kontrolle. Wer eine Typannotation wählt, verzichtet auf Präzision zugunsten von Einfachheit. satisfies verlangt keinen dieser Kompromisse und sollte deshalb in den meisten Fällen die erste Wahl sein.
8. Best Practices für den Einsatz
satisfies eignet sich besonders für Konfigurationsobjekte, Lookup-Tabellen, Theme-Definitionen und alles, bei dem später mit den exakten Literal-Werten weitergearbeitet wird. Für einfache Variablen ohne weitere Verwendung der Literal-Typen reicht oft weiterhin eine normale Annotation.
In Bibliothekscode, der von vielen Konsumenten genutzt wird, hilft satisfies dabei, öffentliche Konstanten so präzise wie möglich zu typisieren, ohne die interne Struktur redundant zu deklarieren.
Auch bei Code-Reviews lohnt sich ein Blick auf bestehende as Assertions: Viele davon lassen sich risikofrei durch satisfies ersetzen und gewinnen dadurch sofort an Sicherheit, ohne dass sich das Laufzeitverhalten der Anwendung in irgendeiner Weise ändert.
9. Fallstricke im Alltag
Ein häufiges Missverständnis ist, dass satisfies den deklarierten Typ der Variable verändert. Das ist nicht der Fall: Der resultierende Typ ist weiterhin der abgeleitete Typ des Ausdrucks, nicht der geprüfte Typ. Wer explizit den geprüften Typ als Variablentyp haben möchte, braucht weiterhin eine zusätzliche Annotation.
Ein zweiter Punkt betrifft die Mindestversion: satisfies erfordert TypeScript 4.9 oder neuer. In älteren Projekten, die noch auf einer früheren Version laufen, führt die Syntax zu einem Parserfehler. Auch die IDE-Autovervollständigung profitiert vom Feature: Beim Tippen innerhalb des Objektliterals schlägt der Editor bereits die Felder des Zieltyps vor, obwohl der resultierende Typ enger bleibt.
Ein dritter Fallstrick betrifft Build-Pipelines mit älteren Transpilern, die zwar den restlichen TypeScript-Code verstehen, aber noch nicht auf dem aktuellen Sprachstand basieren. Ein Upgrade der Toolchain sollte deshalb vor der Einführung von satisfies geprüft werden.
| Merkmal | Typannotation | as Assertion | satisfies |
|---|---|---|---|
| Strukturelle Prüfung | Ja | Nein | Ja |
| Literal-Typen erhalten | Nein | Ja | Ja |
| Fehlt eine Pflichteigenschaft | Fehler | Kein Fehler | Fehler |
| Resultierender Typ der Variable | Deklarierter Typ | Assertierter Typ | Abgeleiteter Typ des Ausdrucks |
| Mindestversion | Alle Versionen | Alle Versionen | TypeScript 4.9+ |
Mironsoft
TypeScript-Migration, Typsicherheit und Team-Onboarding
JavaScript-Codebasis ohne Typsicherheit, aber keine Zeit für eine Rundum-Migration?
Wir migrieren bestehende JavaScript-Projekte schrittweise zu TypeScript, richten strikte Compiler-Einstellungen sauber ein und bringen Teams mit Code-Reviews und Style-Guides auf denselben Typsicherheits-Stand.
Migrations-Fahrplan
Schrittweise JS-zu-TS-Migration ohne Big-Bang-Risiko planen und umsetzen.
Strict-Mode-Einführung
tsconfig.json, ESLint-Regeln und CI-Checks für dauerhafte Typsicherheit aufsetzen.
Team-Onboarding
Entwickler mit Workshops und Code-Reviews in TypeScript-Best-Practices einarbeiten.
10. Zusammenfassung
Satisfies-Operator
Seit Version
TypeScript 4.9
Laufzeit-Overhead
Keiner, reines Compiler-Feature
Kombinierbar mit
as const, Record, Arrays
Typischer Einsatz
Konfigurationsobjekte, Lookup-Tabellen