Object Type Literals beschreiben die Form eines Objekts direkt im Typ-Annotations-Slot — ohne separates interface oder type-Alias. Sie eignen sich für lokale Helfer, Funktions-Parameter und einmalig genutzte Strukturen. TypeScript prüft Object-Literale streng mittels Excess Property Checks, sodass Tippfehler wie colour statt color sofort auffallen. Mit optional- und readonly-Modifikatoren lassen sich Pflicht und Unveränderlichkeit feingranular ausdrücken. Index Signatures öffnen die Tür für dynamische Keys, während die strukturelle Typisierung dafür sorgt, dass Kompatibilität über die Form — nicht über den Namen — entschieden wird. Dieser Artikel zeigt die Mechanik der Object Types und grenzt sie sauber zu interface und type-Alias ab.

Object Type Literals

Ein Object Type Literal ist eine inline-Beschreibung einer Objektform direkt an der Stelle, an der ein Typ erwartet wird. Es benötigt weder einen interface-Header noch einen type-Alias und eignet sich für kurze, lokale Anwendungsfälle.

TypeScript Inline Object Type Literal
function printCoord(pt: { x: number; y: number }): void {
    console.log(`x=${pt.x}, y=${pt.y}`);
}

printCoord({ x: 10, y: 20 });

Die Properties werden durch Komma , oder Semikolon ; getrennt. Das letzte Trennzeichen ist optional. Wird der Typ einer Property weggelassen, fällt sie implizit auf any zurück — was unter strict ein Fehler ist.

TypeScript Trennzeichen-Varianten
type T_KommaVariante = { name: string, age: number };
type T_SemikolonVariante = { name: string; age: number };
type T_Newline = {
    name: string
    age: number
};

Optional Properties

Mit ? nach dem Property-Namen wird eine Property optional. Greift man darauf zu, ist der Typ automatisch T | undefined. Das ist nicht dasselbe wie eine Pflicht-Property mit explizitem undefined-Typ: bei optional darf der Schlüssel komplett fehlen.

TypeScript Optional vs. explizites undefined
type T_Optional = { name?: string };
type T_Explizit = { name: string | undefined };

const a: T_Optional = {};                    // + ok
const b: T_Explizit = {};                    // − Fehler: 'name' fehlt
const c: T_Explizit = { name: undefined };   // + ok

Optionale Properties harmonieren gut mit Default-Werten beim Destructuring.

TypeScript Defaults via Destructuring
function drawPoint({ x = 0, y = 0 }: { x?: number; y?: number }): void {
    console.log(`(${x}, ${y})`);
}

drawPoint({});           // (0, 0)
drawPoint({ x: 5 });     // (5, 0)

Readonly Properties

Mit readonly lässt sich eine Property gegen Neuzuweisung schützen. Der Modifikator wirkt nur an der Oberfläche — verschachtelte Werte bleiben mutierbar. TypeScript kennt kein eingebautes „deep readonly".

TypeScript readonly Grundprinzip
type T_User = {
    readonly id: number;
    name: string;
};

const user: T_User = { id: 1, name: "Anna" };
user.name = "Berta";    // + erlaubt
user.id = 2;            // − Fehler: id ist readonly

Bei verschachtelten Objekten greift readonly nicht in die Tiefe.

TypeScript readonly ist flach
type T_Config = {
    readonly meta: { version: number };
};

const cfg: T_Config = { meta: { version: 1 } };
cfg.meta = { version: 2 };   // − Fehler
cfg.meta.version = 2;        // + erlaubt — nicht tief geschützt

Für echte Unveränderlichkeit kombiniert man readonly mit as const oder dem Utility-Type Readonly<T> — wobei beide ebenfalls nur eine Ebene tief greifen.

Excess Property Checks

Bei der direkten Zuweisung eines Object-Literals an eine typisierte Variable oder einen Parameter führt TypeScript eine zusätzliche Prüfung durch: Properties, die im Zieltyp nicht deklariert sind, lösen einen Fehler aus. Diese „Excess Property Checks" verhindern Tippfehler, die sonst durch die strukturelle Typisierung lautlos durchrutschen würden.

TypeScript Tippfehler werden gefangen
type T_Button = { color: string; size: number };

const btn: T_Button = {
    color: "red",
    size: 10,
    colour: "blue"   // − Fehler: 'colour' existiert nicht in T_Button
};

Es gibt drei Wege, den Check zu umgehen — jeden mit eigenen Trade-offs.

TypeScript Workaround 1 — Zwischenvariable
const draft = { color: "red", size: 10, colour: "blue" };
const btn: T_Button = draft;   // + ok — Excess Check entfällt
TypeScript Workaround 2 — Type Assertion
const btn = {
    color: "red",
    size: 10,
    colour: "blue"
} as T_Button;     // + kompiliert, aber Tippfehler bleibt unerkannt
TypeScript Workaround 3 — Index Signature
type T_Flexible = {
    color: string;
    size: number;
    [key: string]: string | number;
};

const btn: T_Flexible = {
    color: "red",
    size: 10,
    extra: "ok"
};

Index Signatures

Index Signatures beschreiben Objekte mit dynamischen Schlüsseln. Sie definieren, welchen Typ der Schlüssel hat (zulässig: string, number, symbol, Template-Strings oder Unions daraus) und welchen Typ jeder zugehörige Wert tragen muss.

TypeScript Reines Dictionary
type T_StringDict = { [key: string]: string };

const labels: T_StringDict = {
    de: "Hallo",
    en: "Hello",
    fr: "Bonjour"
};

Index Signatures lassen sich mit explizit deklarierten Properties mischen — solange deren Typen kompatibel zur Index-Value-Bedingung sind.

TypeScript Hybrid: feste + dynamische Keys
type T_HybridDict = {
    readonly id: number;
    [key: string]: string | number;
};

const entry: T_HybridDict = {
    id: 1,
    title: "Artikel",
    views: 42
};

Ein Konflikt entsteht, wenn eine explizite Property einen Typ trägt, der nicht zur Index-Signature passt — dann verweigert der Compiler die Definition.

TypeScript Typ-Konflikt
type T_Broken = {
    active: boolean;                  // − Fehler
    [key: string]: string | number;   // boolean nicht zugelassen
};

Als moderne Alternative bietet sich der Utility-Type Record<K, V> an — er ist meist lesbarer und auf den gleichen Anwendungsfall zugeschnitten.

Object Type Literal vs. interface vs. type-Alias

Alle drei Schreibweisen können Objektformen beschreiben — sie unterscheiden sich aber in Erweiterbarkeit, Lesbarkeit und Zweck. Die folgende Tabelle fasst die Unterschiede zusammen.

AspektObject Type Literalinterfacetype-Alias
Syntax{ x: number } inlineinterface I { x: number }type T = { x: number }
Wiederverwendbarnein (anonym)jaja
extends / Vererbung+ (extends)+ (Intersection &)
Declaration Merging+
Unions / Primitives+
Mapped / Conditional Types+
Empfehlunglokale HelferPublic-API, erweiterbarKomposition, Unions

Faustregel: Was eine Bibliothek oder ein Modul nach außen exportiert und Konsumenten erweitern könnten, wird interface. Komposition aus Unions, Intersections oder Mapped Types ist Domäne des type-Alias. Object Type Literals bleiben für inline-Helfer, kleine Parameter-Shapes und Wegwerf-Strukturen reserviert.

Nested Object Types

Object Type Literals lassen sich beliebig verschachteln. Mit zunehmender Tiefe leidet aber die Lesbarkeit — dann hilft es, Teilstrukturen in benannte type-Aliase oder Interfaces auszulagern.

TypeScript Verschachtelt, inline
type T_OrderInline = {
    id: number;
    customer: {
        name: string;
        address: {
            street: string;
            city: string;
            country: string;
        };
    };
    items: { sku: string; qty: number }[];
};

Aufteilung mit benannten Aliasen.

TypeScript Aufgeteilt, benannt
type T_Address = {
    street: string;
    city: string;
    country: string;
};

type T_Customer = {
    name: string;
    address: T_Address;
};

type T_Item = { sku: string; qty: number };

type T_Order = {
    id: number;
    customer: T_Customer;
    items: T_Item[];
};

Die zweite Variante macht Fehlermeldungen klarer und erhöht die Wiederverwendbarkeit.

Methoden-Signaturen

Methoden lassen sich in Object Types auf zwei Weisen schreiben — als Methoden-Signatur oder als Property mit Funktionstyp. Funktional sind beide nahezu gleichwertig; sie unterscheiden sich vor allem bei strictFunctionTypes und der Bi-Varianz von Parametern.

TypeScript Methoden-Signatur
type T_Greeter = {
    greet(name: string): void;
};

const g: T_Greeter = {
    greet(name) {
        console.log(`Hallo, ${name}`);
    }
};
TypeScript Property mit Funktionstyp
type T_GreeterFn = {
    greet: (name: string) => void;
};

const g: T_GreeterFn = {
    greet: (name) => console.log(`Hallo, ${name}`)
};

Empfehlung: Bei striktem Funktions-Typ-Check liefert die Pfeil-Variante stärkere Garantien — Parameter werden ko-/kontra-variant nach den üblichen Regeln geprüft. Methoden-Signaturen sind nachsichtiger, was Legacy-Code entgegenkommt, aber Typsicherheit kostet.

Strukturelle Kompatibilität

TypeScript prüft Typen strukturell („Duck Typing"): zwei Typen sind kompatibel, wenn ihre Form passt — der Name spielt keine Rolle. Wichtig: ein Objekt, das mehr Properties hat als der Zieltyp verlangt, ist grundsätzlich zuweisbar. Die Ausnahme ist genau der oben besprochene Excess Property Check für direkte Object-Literale.

TypeScript Mehr Properties — kein Problem (über Variable)
type T_Point = { x: number; y: number };

const extra = { x: 1, y: 2, z: 3 };
const p: T_Point = extra;     // + ok — extra Properties erlaubt
TypeScript Direktes Literal — Excess Check greift
const p: T_Point = { x: 1, y: 2, z: 3 };   // − Fehler: 'z'

Strukturelle Kompatibilität ist die Grundlage dafür, dass beliebige Objekte gegen einen Typ geprüft werden können, ohne ihn explizit zu implementieren. Genau dieser Mechanismus macht Object Type Literals so praktisch — kein implements, kein Boilerplate, nur Form.

Interessantes

Excess Property Check ist gewollt

Der zusätzliche Check für Object-Literale ist kein Bug, sondern Designentscheidung. Er fängt Tippfehler wie colour statt color ab, die durch die strukturelle Typisierung sonst unbemerkt blieben — besonders bei Optionen-Objekten und Konfigurationen.

Strukturelle Kompatibilität — Extras erlaubt

Nicht-Literal-Quellen (Variablen, Rückgabewerte, Parameter) dürfen mehr Properties haben als der Zieltyp verlangt. Nur das direkte Object-Literal an einer Typgrenze unterliegt dem Excess Check — alles andere folgt purer Strukturprüfung.

Record statt Index Signature

Für simple Dictionaries ist Record<string, string> meist lesbarer als { [key: string]: string }. Beide erzeugen den gleichen Typ; Record dokumentiert die Absicht aber klarer und integriert sich besser in Utility-Type-Ketten.

Style-Guide: interface für Public-APIs, type für Komposition

Der offizielle TypeScript-Stil empfiehlt interface für exportierte, erweiterbare Verträge und type-Aliase für Unions, Intersections und Mapped Types. Object Type Literals bleiben für inline-Helfer reserviert — alles, was wiederverwendet wird, bekommt einen Namen.

Object Types sind nicht der Object-Typ

Ein Object Type Literal wie { x: number } hat nichts mit den eingebauten Typen Object oder object zu tun. Erstere beschreibt eine konkrete Form, letztere sind sehr breit gefasste Marker für „irgendein Objekt".

object — alles außer Primitives

Der Typ object (kleingeschrieben) akzeptiert jeden Wert, der kein Primitive ist — also Objekte, Arrays, Funktionen. Er liefert keinerlei Strukturinfo und ist daher selten direkt nützlich; meist will man einen konkreten Object Type stattdessen.

{} — überraschend permissiv

Der leere Object Type {} akzeptiert jeden Wert außer null und undefined — auch Strings, Zahlen, Booleans. Wer „beliebiges Objekt" meint, braucht object oder besser Record<string, unknown>.

Spread in Type-Definitionen geht NICHT

Anders als bei Werten gibt es keinen Spread-Operator für Typen: type X = { ...A, b: number } ist ungültig. Stattdessen kombiniert man mit Intersection: type X = A & { b: number } — semantisch nah, syntaktisch anders.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Komplexe Typen

Zur Übersicht