Menu

TypeScript Indexed Access Types: T[K], T[number] und mehr

Ein Indexed Access Type liest den Typ einer Eigenschaft aus einem anderen Typ: Person["age"] ist number. T["key"], Union-Schlüssel, T[keyof T], T[number] für Array-Elemente, Tupel-Indizes und (typeof arr)[number], um ein const-Array in eine Union zu verwandeln.

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

Ein Indexed Access Type schlägt den Typ einer Eigenschaft in einem anderen Typ nach, mit derselben Klammersyntax, die JavaScript für den Eigenschaftszugriff verwendet. Person["age"] ist number, und Person["address"]["city"] ist string.

Der Vorteil ist eine einzige Quelle der Wahrheit. Age ist keine zweite Kopie von number: Ändert sich Person["age"] zu string, ändert sich jeder Typ mit, der als Person["age"] geschrieben ist. Indexed Access Types existieren nur beim Kompilieren; sie erzeugen kein JavaScript.

Union-Schlüssel und T[keyof T]

Der Index kann eine Union von Schlüsseln sein. Das Ergebnis ist die Union der Eigenschaftstypen:

Product[keyof Product] heißt auf Typebene "irgendein Eigenschaftswert von Product". Einen Schlüssel abzufragen, den es nicht gibt, ist ein Compilerfehler: Product["price"] ergibt error TS2339: Property 'price' does not exist on type 'Product'.

Optionale Eigenschaften enthalten undefined

Mit strictNullChecks (unter strict aktiv) enthält der Typ einer optionalen Eigenschaft undefined, und ihr Indexed Access Type ebenso:

type Profile = { name: string; nickname?: string };

type Nick = Profile["nickname"]; // string | undefined

Um das undefined loszuwerden, umschließe den Typ: NonNullable<Profile["nickname"]> ist string.

Elementtypen von Arrays mit T[number]

Arrays werden mit Zahlen indiziert, also ergibt ein Array-Typ, der mit number indiziert wird, den Elementtyp. Das funktioniert auch über mehrere Ebenen:

Das ist nützlich, wenn ein Typ aus einer Quelle kommt, die du nicht kontrollierst, etwa einem generierten API-Client: ApiResponse["data"]["users"][number] benennt den User-Typ, ohne dass ihn jemand exportiert haben muss.

Tupel: Indizieren nach Position

Einen Tupel-Typ kannst du mit einer bestimmten Position indizieren, mit number für die Union aller Elementtypen und mit "length" für seine Länge als Literaltyp:

(typeof arr)[number]: eine Union aus einem const-Array

Der häufigste Einsatz in der Praxis: Du hältst eine Liste erlaubter Werte als Array (damit du zur Laufzeit darüber iterieren kannst) und leitest daraus den Union-Typ ab (damit der Compiler ihn prüft).

Zwei Teile sorgen dafür, dass es funktioniert. as const macht das Array zu einem readonly-Tupel aus Literaltypen; ohne das ist ROLES ein string[] und (typeof ROLES)[number] nur string. Dann macht typeof aus dem Wert einen Typ, und [number] sammelt seine Elemente in einer Union. Die Klammern sind optional (typeof ROLES[number] bedeutet dasselbe), machen aber die Reihenfolge der Operationen deutlich.

Einen String gegen das Array prüfen

Dieses readonly-Tupel hat eine Überraschung parat. Seine Methode includes akzeptiert nur den Elementtyp, also kannst du ihr keinen beliebigen String übergeben:

index.ts(6,25): error TS2345: Argument of type 'string' is not assignable to parameter of type '"admin" | "editor" | "viewer"'.

Erweitere das Array für die Prüfung auf readonly string[] und mach aus der Funktion einen Type Guard, damit eine erfolgreiche Prüfung den Wert eingrenzt:

Mit diesem Muster hast du eine Liste für beide Aufgaben: Die Laufzeitprüfung nutzt das Array, und der Typ wird daraus abgeleitet, also können beide nicht auseinanderlaufen.

Indexed Access in Generics: T[K]

Innerhalb eines Generics ist T[K] mit K extends keyof T der Typ der Eigenschaft, die der Aufrufer gewählt hat:

function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
  return items.map((item) => item[key]);
}

const users = [
  { name: "Ada", age: 36 },
  { name: "Linus", age: 28 },
];

const names = pluck(users, "name"); // string[]
const ages = pluck(users, "age");   // number[]

Erst der Constraint K extends keyof T macht T[K] zulässig. Ohne ihn kann TypeScript nicht wissen, dass K ein Schlüssel von T ist, und meldet error TS2536: Type 'K' cannot be used to index type 'T'.

Syntax im Überblick

GeschriebenBedeutungBeispielergebnis
T["key"]Typ einer EigenschaftPerson["age"] ist number
T["a" | "b"]Union dieser Eigenschaftstypenstring | number
T[keyof T]Union aller Eigenschaftstypenjeder Werttyp
T["a"]["b"]verschachtelte EigenschaftPerson["address"]["city"]
Arr[number]Elementtyp eines ArraysUser[] ergibt User
Tup[0]Tupel-Element an einer PositionTyp des ersten Elements
Tup["length"]Tupel-Länge als Literal3
(typeof arr)[number]Union der Werte eines const-Arrays"admin" | "editor"
T[typeof key]Index mit dem Typ einer Konstantewie T["name"]

Der Index ist immer ein Typ. Person[key], wobei key eine const-Variable ist, scheitert mit zwei Fehlern: error TS2538: Type 'key' cannot be used as an index type. und error TS2749: 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?.

Häufig gestellte Fragen

Was ist ein Indexed Access Type in TypeScript?

Ein Typ der Form T[K], der den Typ der Eigenschaft K im Typ T nachschlägt. Mit type Person = { name: string; age: number } ist Person["age"] gleich number. Er nutzt dieselbe Klammersyntax wie der Eigenschaftszugriff in JavaScript, arbeitet aber beim Kompilieren mit Typen.

Wie bekomme ich den Typ eines Array-Elements in TypeScript?

Indiziere den Array-Typ mit number: Für type Users = User[] ist Users[number] gleich User. Für einen Array-Wert kombinierst du das mit typeof: (typeof users)[number].

Wie mache ich aus einem Array von Strings einen Union-Typ?

Deklariere das Array mit as const, damit TypeScript die Literalwerte behält, und indiziere es dann mit number: const roles = ["admin", "editor"] as const; type Role = (typeof roles)[number]; ergibt "admin" | "editor". Ohne as const ist der Elementtyp nur string.

Warum kann ich keine Variable als Index in einem Indexed Access Type verwenden?

Der Index muss ein Typ sein. Person[key] mit const key = "name" scheitert (Fehler TS2538 und TS2749), weil key ein Wert ist. Schreibe Person[typeof key] oder verwende das Literal direkt: Person["name"].

Was bedeutet T[keyof T]?

Es indiziert T mit der Union aller seiner Schlüssel, das Ergebnis ist also die Union aller seiner Eigenschaftstypen. Für { a: string; b: number } ist es string | number. Auf Typebene heißt das "irgendein Wert dieses Objekts".

Illustration der Programmiersprachen bei Coddy

Lerne mit Coddy zu programmieren

Los geht's