as const: Const-Assertions für literale, unveränderliche Typen
AI generated
type
TypeScript
as const richtig einsetzen
Const-Assertions für literale, unveränderliche Typen

as const zwingt den Compiler, den engstmöglichen literalen Typ für einen Ausdruck abzuleiten, statt ihn auf einen weiten Typ wie string oder number[] zu verallgemeinern. Das Ergebnis sind präzisere Typen, ganz ohne zusätzliche Typdefinition.

10 Min. Lesezeit TypeScript Typsystem

1. Was as const mit einem Ausdruck macht

Ohne Const-Assertion leitet TypeScript für ein Literal den weitestmöglichen passenden Typ ab: Aus let status = "active" wird der Typ string, nicht das literale "active". Diese Verbreiterung ist sinnvoll, weil eine let-Variable später neu zugewiesen werden könnte.

Mit as const hinter einem Ausdruck weist man den Compiler an, genau das Gegenteil zu tun: den engstmöglichen, literalen Typ abzuleiten und alle enthaltenen Eigenschaften implizit als readonly zu markieren.

Der Effekt betrifft nicht nur einzelne Werte, sondern rekursiv ganze Ausdrucksbäume: Arrays werden zu readonly Tupeln mit exakten Elementtypen, Objekte erhalten readonly Properties mit literalen statt verallgemeinerten Werten.


let a = "active";        // Typ: string
let b = "active" as const; // Typ: "active"

const status = { code: 200, label: "ok" };
// Typ: { code: number; label: string }

const statusConst = { code: 200, label: "ok" } as const;
// Typ: { readonly code: 200; readonly label: "ok" }

2. Array-Literale mit as const als Tupel statt Array

Ein normales Array-Literal wird zu einem generischen, veränderlichen Array-Typ verallgemeinert, etwa string[] für eine Liste von Farbnamen. Die Reihenfolge und Länge der Elemente geht dabei vollständig verloren.

Mit as const wird aus demselben Literal ein readonly Tupel mit exakten literalen Elementtypen an jeder Position. Das ist besonders nützlich, wenn eine feste Liste erlaubter Werte gleichzeitig als Laufzeitwert und als Typquelle dienen soll.

Über typeof und den Indexzugriff [number] lässt sich aus einem solchen Tupel direkt eine Union aus allen enthaltenen literalen Werten ableiten, ohne die Liste ein zweites Mal als Typ zu wiederholen.


const themes = ["light", "dark", "system"] as const;
// Typ: readonly ["light", "dark", "system"]

type Theme = (typeof themes)[number];
// Typ: "light" | "dark" | "system"

function setTheme(theme: Theme) {
  document.documentElement.dataset.theme = theme;
}

3. Objekt-Literale: readonly Properties und literale Werte

Bei Objekten sorgt as const dafür, dass jede Property als readonly markiert wird und ihr Wert, sofern es sich um ein primitives Literal handelt, als exakter literaler Typ statt als verallgemeinerter Typ wie number oder string erscheint.

Verschachtelte Objekte und Arrays innerhalb des Literals werden ebenfalls rekursiv erfasst: Ein Konfigurationsobjekt mit mehreren Ebenen wird komplett unveränderlich typisiert, ohne dass jede Ebene manuell mit Readonly umschlossen werden müsste.

Das eignet sich gut für Konstanten, die als einzige Quelle der Wahrheit für erlaubte Werte, Konfigurationen oder Routing-Tabellen dienen, weil Tippfehler in abgeleiteten Typen sofort als Compile-Fehler sichtbar werden.

4. as const in Kombination mit Union Types statt enum

Viele Teams verwenden mittlerweile ein Objekt mit as const statt eines enum, um eine feste Menge an Werten zu definieren. Der Vorteil: Es entsteht kein zusätzlicher Laufzeit-Konstrukt wie bei numerischen Enums, sondern ein reines Objekt mit literalen Werten.

Aus diesem Objekt lässt sich über typeof und keyof sowohl ein Union-Typ der Schlüssel als auch der Werte ableiten, was in der Praxis flexibler ist als ein klassisches TypeScript-Enum mit seinen eigenen Sonderregeln.

Ein weiterer Vorteil gegenüber String-Enums: Die Werte bleiben normale, unbenannte String-Literale, die sich problemlos mit externen APIs, JSON-Daten oder Datenbank-Werten vergleichen lassen, ohne über einen Enum-Umweg gehen zu müssen.


const OrderStatus = {
  Pending: "pending",
  Shipped: "shipped",
  Delivered: "delivered",
} as const;

type OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];
// Typ: "pending" | "shipped" | "delivered"

function isFinal(status: OrderStatus): boolean {
  return status === OrderStatus.Delivered;
}

5. as const und satisfies gemeinsam nutzen

as const allein liefert den engstmöglichen Typ, prüft aber nicht, ob dieser Typ zu einer erwarteten Form passt. Genau hier ergänzt der satisfies-Operator: Er validiert einen Ausdruck gegen einen Zieltyp, ohne den abgeleiteten literalen Typ zu verbreitern.

Die Kombination { ... } satisfies Record as const ist in dieser Reihenfolge nicht gültig, weshalb satisfies typischerweise nach dem Objektliteral steht und as const weggelassen wird, sobald ein satisfies-Constraint bereits die Struktur prüft.

In der Praxis ersetzt satisfies viele Fälle, in denen früher as const allein für Konfigurationsobjekte verwendet wurde, weil zusätzlich ein Fehler auftritt, sobald ein Schlüssel fehlt oder ein Wert nicht zum erwarteten Typ passt.


type RouteConfig = { path: string; auth: boolean };

const routes = {
  dashboard: { path: "/dashboard", auth: true },
  login: { path: "/login", auth: false },
} satisfies Record<string, RouteConfig>;

// routes.dashboard.path bleibt literal "/dashboard", nicht nur string

6. Praxisbeispiel: Konfigurationsobjekte und Action-Typen

In State-Management-Bibliotheken wie Redux oder in eigenen Reducer-Implementierungen dient as const häufig dazu, Action-Objekte mit einem literalen type-Feld zu erzeugen, das der Compiler für Discriminated Unions im switch-Statement auswerten kann.

Ohne as const würde das Feld type zu string verbreitert, wodurch TypeScript im switch keine erschöpfende Prüfung mehr durchführen und keine spezifischen Payload-Typen pro Fall ableiten könnte.

Auch API-Client-Konfigurationen mit festen Endpunkt-Pfaden profitieren davon: Ein zentrales, mit as const definiertes Objekt dient gleichzeitig als Laufzeitwert für HTTP-Aufrufe und als Typquelle für alle erlaubten Routen.


function increment(amount: number) {
  return { type: "increment", amount } as const;
}
function reset() {
  return { type: "reset" } as const;
}

type Action = ReturnType<typeof increment> | ReturnType<typeof reset>;

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case "increment":
      return state + action.amount;
    case "reset":
      return 0;
  }
}

7. Grenzen von as const: keine Laufzeit-Garantie

as const ist ein reines Compile-Zeit-Konstrukt. Zur Laufzeit bleibt das Objekt oder Array ein vollkommen normaler, veränderlicher JavaScript-Wert. Nur der Compiler verweigert Zuweisungen an readonly-markierte Properties.

Wer eine echte Laufzeit-Absicherung gegen Mutation benötigt, etwa weil ein Objekt aus nicht-TypeScript-Code heraus verändert werden könnte, muss zusätzlich Object.freeze() einsetzen, das die Mutation zur Laufzeit tatsächlich verhindert oder zumindest im Strict Mode einen Fehler wirft.

In der Praxis reicht die reine Compile-Zeit-Absicherung für die meisten internen Anwendungsfälle aus, weil Mutationsversuche durch eigenen TypeScript-Code ohnehin schon während der Entwicklung als Fehler auffallen, bevor sie in Produktion gelangen.

8. as const, Readonly und Object.freeze im Vergleich

Readonly<T> wandelt einen bestehenden Typ in eine readonly Variante um, wirkt aber nur eine Ebene tief und verbreitert literale Werte nicht automatisch, während as const beides gleichzeitig erledigt, direkt am Ausdruck selbst.

Object.freeze() ist reines Laufzeitverhalten ohne jede Auswirkung auf die von TypeScript inferierten Typen, sofern es nicht mit einem generischen Rückgabetyp kombiniert wird, der die Readonly-Semantik zusätzlich in die Typebene überträgt.

In produktivem Code werden alle drei häufig kombiniert: as const für literale, unveränderliche Konfiguration im Quellcode, Readonly<T> für Funktionsparameter, die Mutation verhindern sollen, und Object.freeze() dort, wo tatsächlich Laufzeitschutz nötig ist.

9. Typische Fehler beim Einsatz von as const

Ein häufiger Fehler ist, as const auf eine Variable anzuwenden, die anschließend noch mit let deklariert wird: Der literale, readonly Typ verhindert dann jede spätere Zuweisung, selbst wenn let das eigentlich erlauben würde.

Ein zweiter Fallstrick betrifft kontextuelle Typisierung: Wird ein mit as const erzeugtes Objekt an eine Funktion übergeben, die einen weiteren, veränderlichen Typ erwartet, kann TypeScript den readonly-Typ zurückweisen, weil eine readonly Struktur nicht automatisch einer veränderlichen Struktur zuweisbar ist.

In diesen Fällen hilft entweder eine explizite Typannotation an der Zielstelle oder der gezielte Verzicht auf as const zugunsten von satisfies, das die Struktur prüft, ohne die spätere Zuweisbarkeit an veränderliche Typen zu blockieren.

Merkmal as const Readonly Object.freeze()
Wirkt auf Ausdruck direkt am Ort der Definition Bestehenden Typ Laufzeitobjekt
Tiefe Rekursiv über den gesamten Ausdruck Nur eine Ebene tief Nur eine Ebene tief (ohne Zusatzcode)
Literale Typen Ja, verhindert Verbreiterung Nein, ändert nur Mutierbarkeit Nein, reine Laufzeitwirkung
Laufzeit-Effekt Keiner Keiner Verhindert Mutation tatsächlich
Typischer Einsatz Konstante Konfigurationen, Tupel, Action-Typen Funktionsparameter, API-Signaturen Sicherheitskritische, unveränderliche Objekte

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

as const

Literale Typen

Engstmöglicher Typ statt Verbreiterung

Readonly rekursiv

Gilt für verschachtelte Arrays und Objekte

Nur Compile-Zeit

Keine Laufzeit-Garantie gegen Mutation

Enum-Alternative

Objekt plus as const statt enum

11. FAQ: as const

1Was ist der Unterschied zwischen as const und einer normalen Typannotation?
Eine normale Typannotation weist einen bestimmten Typ zu, ohne ihn abzuleiten. as const leitet stattdessen den engstmöglichen literalen Typ direkt aus dem Ausdruck ab, ohne dass dieser Typ manuell geschrieben werden muss.
2Macht as const ein Objekt zur Laufzeit unveränderlich?
Nein. as const wirkt ausschließlich auf der Typebene. Zur Laufzeit bleibt das Objekt ein normaler, veränderlicher JavaScript-Wert, es sei denn, zusätzlich wird Object.freeze() eingesetzt.
3Warum wird ein Array mit as const zu einem Tupel statt zu Array?
Ohne as const verallgemeinert TypeScript ein Array-Literal zu einem veränderlichen Array-Typ mit einem gemeinsamen Elementtyp. as const bewahrt dagegen Länge, Reihenfolge und literale Typen jeder einzelnen Position.
4Kann ich as const und satisfies gleichzeitig verwenden?
Beide lassen sich kombinieren, allerdings prüft satisfies bereits die Struktur gegen einen Zieltyp, ohne literale Typen zu verbreitern. In der Praxis ersetzt satisfies daher oft den alleinigen Einsatz von as const für Konfigurationsobjekte.
5Ist ein Objekt mit as const eine gute Alternative zu enum?
Ja, in vielen Fällen. Es erzeugt keinen zusätzlichen Laufzeit-Konstrukt wie ein numerisches Enum und liefert über typeof und keyof direkt einen passenden Union-Typ der Werte.
6Warum lehnt TypeScript eine spätere Zuweisung nach as const ab?
Weil as const alle Properties implizit als readonly markiert. Jede Zuweisung an eine solche Property gilt als Verstoß gegen die Readonly-Regel, unabhängig davon, ob die Variable mit let oder const deklariert wurde.
7Wie leite ich aus einem as-const-Array einen Union-Typ ab?
Über typeof gefolgt vom Indexzugriff [number] auf das Array. Das Ergebnis ist eine Union aus allen literalen Elementtypen des Tupels, ohne die Werte ein zweites Mal manuell aufzulisten.
8Warum verweigert TypeScript manchmal die Zuweisung eines as-const-Objekts an einen Parameter?
Weil ein readonly-Typ nicht automatisch einer veränderlichen Struktur zuweisbar ist. In diesem Fall hilft eine explizite Typannotation am Zielort oder der Verzicht auf as const zugunsten von satisfies.
9Funktioniert as const auch bei verschachtelten Objekten?
Ja, die Wirkung ist rekursiv. Jede Ebene eines verschachtelten Objekt- oder Array-Literals wird als readonly markiert und erhält, wo möglich, literale statt verallgemeinerte Typen.
10Wann sollte ich lieber Object.freeze() statt as const verwenden?
Immer dann, wenn tatsächlicher Laufzeitschutz gegen Mutation nötig ist, etwa weil ein Objekt auch von nicht-typgeprüftem Code oder externen Bibliotheken verändert werden könnte.