Einleitung: Warum meckert TypeScript Ihren Code an?
\n\nHaben Sie schon einmal TypeScript-Code geschrieben, der völlig logisch erscheint, der Compiler aber plötzlich einen erschreckenden Fehler ausgibt? Wenn Sie auf die Meldung «An index signature parameter type cannot be a union type. Consider using a mapped object type instead» gestoßen sind – Sie sind nicht allein. Dieser Fehler ist eine der häufigsten Fragen auf Stack Overflow (tausende Aufrufe!) und bringt selbst erfahrene Entwickler oft ins Grübeln.
\n\nIn diesem Artikel zeigen wir nicht nur, wie man den Fehler behebt. Wir analysieren, warum TypeScript so streng ist, und wie Sie mapped object types verwenden, um Ihren Code nicht nur funktionsfähig, sondern auch sicherer zu machen. Los geht's!
\n\nWas ist ein index signature und wozu braucht man ihn?
\n\nStellen Sie sich vor, Sie erstellen ein Objekt, dessen Schlüssel Strings und dessen Werte Zahlen sind. Zum Beispiel ein Wörterbuch mit Produktpreisen:
\n\n// Normales Objekt\nconst prices = {\n apple: 100,\n banana: 50,\n cherry: 200\n};\n\nWas aber, wenn Sie TypeScript sagen wollen: «Hör zu, dieses Objekt kann beliebige String-Schlüssel haben, und alle Werte sind Zahlen»? Dafür braucht man den index signature – eine spezielle Syntax, die ein Muster für alle Eigenschaften eines Objekts beschreibt:
\n\n// Index signature: [key: string]: number\ninterface PriceMap {\n [key: string]: number;\n}\n\nconst prices: PriceMap = {\n apple: 100,\n banana: 50\n};\n\nHier ist [key: string]: number der index signature. Er besagt: «Schlüssel sind Strings, Werte sind Zahlen». Ganz einfach, oder?
Und wo liegt das Problem mit Union-Typen?
\n\nStellen Sie sich nun vor, Sie möchten die möglichen Schlüssel auf nur zwei Optionen beschränken: 'name' oder 'age'. Es scheint logisch, das so zu schreiben:
// ❌ FEHLER! So geht das nicht\ninterface Person {\n [key: 'name' | 'age']: string | number;\n}\n\nUnd dann gibt TypeScript genau diesen Fehler aus: «An index signature parameter type cannot be a union type. Consider using a mapped object type instead».
\n\nWarum? Weil der index signature dazu gedacht ist, eine unbegrenzte Menge von Schlüsseln zu beschreiben. Wenn Sie eine konkrete Liste von Schlüsseln angeben möchten, ist das kein Muster mehr, sondern eine genaue Beschreibung der Struktur. TypeScript erlaubt es nicht, diese Konzepte zu vermischen.
\n\nWie behebt man den Fehler? Verwenden Sie Mapped Object Type
\n\nDie Lösung, die der Compiler selbst vorschlägt, ist die Verwendung eines mapped object type (zugeordneter Objekttyp). Das klingt kompliziert, ist aber in der Praxis nur eine Möglichkeit, alle Schlüssel aus einem Union-Typ zu «durchlaufen» und für jeden eine eigene Eigenschaft zu erstellen.
\n\nSo sieht das aus:
\n\n// ✅ RICHTIG: mapped type verwenden\ntype Person = {\n [K in 'name' | 'age']: K extends 'name' ? string : number;\n};\n\n// Oder eine einfachere Variante, wenn die Werttypen gleich sind:\ntype PersonSimple = {\n [K in 'name' | 'age']: string | number;\n};\n\nAnalysieren wir die Syntax [K in 'name' | 'age']:
- \n
K– eine Variable, die nacheinander jeden Wert aus dem Union-Typ annimmt ('name', dann'age'). \n in– ein Schlüsselwort, das eine «Schleife» über die Typen startet. \n 'name' | 'age'– unsere Menge von Schlüsseln. \n
Als Ergebnis erstellt TypeScript ein Objekt mit zwei konkreten Eigenschaften: name und age. Der Fehler ist verschwunden!
Beispiel mit unterschiedlichen Werttypen
\n\nOft kommt es vor, dass verschiedene Schlüssel unterschiedliche Werttypen haben sollen. Mit bedingten Typen (conditional types) lässt sich das elegant lösen:
\n\ntype Config = {\n [K in 'host' | 'port' | 'debug']: \n K extends 'host' ? string :\n K extends 'port' ? number :\n boolean;\n};\n\n// Jetzt: