Menu

TypeScript satisfies: vs Typannotation und as

Der Operator satisfies prüft, ob ein Wert zu einem Typ passt, ohne den abgeleiteten Typ des Werts zu ändern. Was er tut, wie er sich zu einer Typannotation und zu as verhält (dasselbe Objekt auf drei Arten geschrieben), wie er sich mit as const kombinieren lässt und warum er zu Konfigurationsobjekten passt.

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

value satisfies Type prüft beim Kompilieren, dass value zu Type passt, und lässt dann den eigenen, genaueren Typ des Werts in Ruhe. Eine Annotation würde diesen genauen Typ durch Type ersetzen; satisfies validiert, ohne zu erweitern.

satisfies prüft trotzdem: Eine fehlende Farbe, ein falsch geschriebener Schlüssel wie bleu oder ein Wert wie true ist in dieser Zeile ein Compilerfehler. Es gibt satisfies seit TypeScript 4.9, und wie jede Typannotation wird es aus dem erzeugten JavaScript entfernt.

Das Problem, das satisfies löst

Mit einer Typannotation ist der Typ der Variablen die Annotation. Der Compiler vergisst, was er im Literal gesehen hat. Hier wird dieselbe Palette stattdessen annotiert, und nun weiß TypeScript nicht mehr, dass green ein String ist:

Der Compiler meldet:

index.ts(11,27): error TS2339: Property 'toUpperCase' does not exist on type 'Color'.
  Property 'toUpperCase' does not exist on type '[number, number, number]'.

Vor TypeScript 4.9 blieb die Wahl: annotieren und überall von Hand einengen (typeof palette.green === "string") oder die Annotation weglassen und die Prüfung verlieren. satisfies bietet beides. Ändere : Record<ColorName, Color> zu satisfies Record<ColorName, Color> hinter der schließenden geschweiften Klammer, und es läuft.

satisfies vs Typannotation vs as

Dasselbe Einstellungsobjekt, auf drei Arten geschrieben:

as hat das fehlende lang durchgelassen, und asserted.lang ist zur Laufzeit undefined, während sein Typ string sagt. Entferne lang aus den anderen beiden Zeilen, und beide scheitern mit TS2741, Property 'lang' is missing in type ....

Annotation const x: T = vAssertion v as Tv satisfies T
Fehlende EigenschaftenFehlererlaubtFehler
Zusätzliche Eigenschaften (Objektliteral)FehlererlaubtFehler
Falscher EigenschaftstypFehlernur wenn sich die Typen nicht überschneidenFehler
Typ von x danachTTder abgeleitete Typ von v
Literaltypen ("dark", 8080)auf T erweitertauf T erweitertbehalten, soweit T sie erlaubt
Schlüssel eines Record<string, ...>jeder String (Tippfehler kompilieren)jeder Stringgenau die geschriebenen Schlüssel
Wirkung zur Laufzeitkeinekeinekeine

Faustregel: Annotiere, wenn die Variable den deklarierten Typ haben soll (ein Wert, den du neu zuweist, eine öffentliche API), und nimm satisfies, wenn du eine Prüfung willst, der eigene Typ des Werts aber nützlicher ist.

Fehler in Objektliteralen finden

satisfies führt die vollständige Prüfung auf Zuweisbarkeit durch, einschließlich Excess Property Checks, Tippfehler in Schlüsseln sind also Fehler:

type Route = { path: string; method: "GET" | "POST" };

const home = { path: "/", metod: "GET" } satisfies Route;
// error TS2561: Object literal may only specify known properties, but 'metod' does not exist in type 'Route'. Did you mean to write 'method'?

Die Prüfung gibt dem Literal auch einen kontextuellen Typ, genau wie eine Annotation. Das zählt in zweierlei Hinsicht. String-Literale bleiben Literaltypen, wenn der Zieltyp sie erwartet: { path: "/", method: "GET" } satisfies Route hat method: "GET", während dasselbe Objekt ohne Annotation method: string ableiten würde. Und Callback-Parameter werden aus dem Zieltyp abgeleitet:

Record-Schlüssel bleiben bekannt

Ein häufiger Einsatz ist eine Lookup-Tabelle. Als Record<string, T> annotiert ist jeder String ein gültiger Schlüssel, und ein Tippfehler kompiliert und liefert zur Laufzeit undefined. Mit satisfies werden die Werte weiterhin gegen T geprüft, aber der Typ der Variablen führt genau die Schlüssel auf, die du geschrieben hast:

keyof typeof endpoints ist nur nützlich, weil die Schlüssel erhalten geblieben sind. Mit der Annotation wäre es einfach string.

Um eine feste Menge von Schlüsseln zu verlangen, erfülle einen Record über eine Union: satisfies Record<"dev" | "prod", string> meldet ein fehlendes prod mit TS2741 und ein unbekanntes staging mit TS2353.

as const satisfies

as const und satisfies lassen sich kombinieren. Schreibe as const zuerst: Es macht den Wert tief readonly mit Literaltypen, dann prüft satisfies genau diesen Wert.

Jede Route wird gegen Route geprüft (ein method: "PUT" wäre ein Fehler), und das Tupel aus Literaltypen bleibt verfügbar, sodass Path eine Union der echten Pfade ist. Nimm readonly Route[] (oder ReadonlyArray<Route>) als Ziel, da ein Array mit as const readonly ist.

Konfigurationsobjekte

Bei Konfigurationen zeigt satisfies seinen Wert: Die Form muss stimmen, und Code an anderer Stelle braucht die genauen Werte.

Vergiss den Eintrag production, schreib logLevel falsch oder setze logLevel: "verbose", und der Compiler zeigt auf genau die Zeile. Dasselbe Muster passt zu *.config.ts-Dateien: export default { ... } satisfies SomeConfig prüft die ganze Datei, während das exportierte Objekt seine Literalwerte behält.

Wann man satisfies nicht verwendet

  • Die Variable wird neu zugewiesen. let cfg = { port: 3000 } satisfies { port: number | string } gibt cfg den Typ { port: number }, also scheitert ein späteres cfg = { port: "80" } (TS2322). Annotiere Variablen, die du ändern willst.
  • Du willst absichtlich den deklarierten Typ. Bei einem Rückgabewert oder einer exportierten Konstante, die Teil einer API ist, ist der Typ der Annotation der Vertrag, und den genauen Literaltyp preiszugeben kann spätere Änderungen zu Breaking Changes machen.
  • Der Wert ist kein Literal. satisfies glänzt bei Objekt und Array-Literalen. Bei einer Variablen oder einem Funktionsergebnis ist es eine einfache Prüfung auf Zuweisbarkeit, die eine Annotation bereits liefert.

Häufig gestellte Fragen

Was macht satisfies in TypeScript?

expression satisfies Type prüft beim Kompilieren, dass der Ausdruck Type zuweisbar ist, meldet fehlende Eigenschaften, zusätzliche Eigenschaften und falsche Werttypen und lässt dann den eigenen abgeleiteten Typ des Ausdrucks unverändert. Du bekommst die Sicherheit einer Annotation und die Genauigkeit der Inferenz. Es wird aus der JavaScript-Ausgabe entfernt.

Was ist der Unterschied zwischen satisfies und einer Typannotation?

Beide prüfen den Wert. Eine Annotation (const x: T = ...) gibt der Variablen danach den Typ T und vergisst, was der Compiler über den Wert wusste (Literaltypen, welcher Union-Member jede Eigenschaft ist, welche Schlüssel existieren). satisfies T behält den abgeleiteten Typ, also ist bekannt, dass x.someKey existiert, und eine Eigenschaft vom Typ string | number, die einen String enthält, ist als string typisiert.

Was ist der Unterschied zwischen satisfies und as in TypeScript?

as ist eine Assertion: Sie überschreibt den Typ und prüft fast nichts, fehlende Eigenschaften bleiben also unbemerkt. satisfies ist eine Prüfung: Der Wert muss wirklich zum Typ passen, und sein eigener abgeleiteter Typ bleibt erhalten. Wenn beides kompilieren würde, ist satisfies die sicherere Wahl.

Was bedeutet as const satisfies?

Es wendet beides an: as const macht den Wert tief readonly mit Literaltypen, dann prüft satisfies dieses Ergebnis gegen einen Typ. Schreibe as const zuerst: const routes = [...] as const satisfies readonly Route[];. Die Variable behält die genauen Literaltypen für die spätere Verwendung, und ein falscher Eintrag ist trotzdem ein Compilerfehler.

Mit welcher TypeScript-Version kam satisfies?

Mit TypeScript 4.9, veröffentlicht im November 2022. Es ist einfach entfernbare Syntax, läuft also auch mit dem eingebauten Type Stripping von Node, und jede aktuelle TypeScript-Version (einschließlich 7) unterstützt es.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S