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.
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.
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.
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 }; // + okOptionale Properties harmonieren gut mit Default-Werten beim 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".
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 readonlyBei verschachtelten Objekten greift readonly nicht in die Tiefe.
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ütztFü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.
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.
const draft = { color: "red", size: 10, colour: "blue" };
const btn: T_Button = draft; // + ok — Excess Check entfälltconst btn = {
color: "red",
size: 10,
colour: "blue"
} as T_Button; // + kompiliert, aber Tippfehler bleibt unerkannttype 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.
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.
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.
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.
| Aspekt | Object Type Literal | interface | type-Alias |
|---|---|---|---|
| Syntax | { x: number } inline | interface I { x: number } | type T = { x: number } |
| Wiederverwendbar | nein (anonym) | ja | ja |
extends / Vererbung | − | + (extends) | + (Intersection &) |
| Declaration Merging | − | + | − |
| Unions / Primitives | − | − | + |
| Mapped / Conditional Types | − | − | + |
| Empfehlung | lokale Helfer | Public-API, erweiterbar | Komposition, 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.
type T_OrderInline = {
id: number;
customer: {
name: string;
address: {
street: string;
city: string;
country: string;
};
};
items: { sku: string; qty: number }[];
};Aufteilung mit benannten Aliasen.
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.
type T_Greeter = {
greet(name: string): void;
};
const g: T_Greeter = {
greet(name) {
console.log(`Hallo, ${name}`);
}
};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.
type T_Point = { x: number; y: number };
const extra = { x: 1, y: 2, z: 3 };
const p: T_Point = extra; // + ok — extra Properties erlaubtconst 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
- Object Types – TypeScript Handbook
- Everyday Types – TypeScript Handbook
- Differences between Type Aliases and Interfaces – Handbook