Indexed access types

Explains indexed access types, T[K], which look up the value type stored at a named key of T. Covers the rules that the index must be a bounded named key and T a decidable object type, and the role T[K] plays in copying property types inside a mapped type.

An indexed access type T[K] looks up the value type stored at a named key of T. If Person has a name: string property, Person["name"] is string.

type Person = { id: number; name: string }
type N = Person["name"]   // string

This is the type-level counterpart of reading a property off an object. Instead of a value, you get the type at that key, computed at compile time. It is the same operator as TypeScript's indexed access, with tighter rules about what counts as a valid index.

What the index and target must be

Two conditions have to hold for a lookup to resolve:

  • The index must be a bounded named key — a specific key literal (or a union of them) that T actually declares. A bare string or number index lookup, the kind you would use to read an index signature, is rejected. You look up known keys, not arbitrary ones.
  • T must be a decidable object type — a shape the checker can resolve concretely enough to find the key.

If the target cannot be decided, that is a compile-time error. The intent is that a missing key is rejected too — a lookup should name a real key of a known shape rather than fall back to unknown or undefined. Note, however, that the missing-key check is not yet enforced: today the checker accepts a lookup on a key that T does not declare. Do not rely on a missing key being caught until enforcement lands.

type Person = { id: number; name: string }
type N = Person["name"]     // string
type X = Person["email"]    // intended error (no such key); not yet enforced — currently accepted

Its role inside mapped types

Indexed access is the copy operation at the heart of a homomorphic mapped type. When a mapping walks a source's keys and writes T[K] as each key's value type, it is reading the original property type back out through an indexed access:

{ [K in keyof T]: T[K] }

Because this is the mechanism that carries each property's type across, it is also part of what lets a homomorphic mapping preserve the source's per-property modifiers. The lookup copies the value type; the mapping copies the readonly and optional flags alongside it.

Why it works this way

A lookup should name something the checker can find. Requiring a bounded named key, and a decidable target, means every T[K] refers to a property that provably exists in a shape that is provably known. A missing key is a mistake worth reporting, not a value to invent — the aim is an error rather than a silent undefined (though that missing-key rejection is not yet enforced; see above). The goal is to keep indexed access honest: the type you get back is the real type of a real key.

Limitations

  • Single bounded-named-key lookups only, today. Looking up one named key (or a small union of named keys) of a decidable object works now.
  • Bare index-signature lookups are not supported. T[string] and T[number] style lookups against an index signature are not available.
  • Missing-key rejection is not yet enforced. A lookup on a key T does not declare is intended to be a compile-time error, but the checker currently accepts it. Do not rely on this catch until it lands.
  • Direct union-index aliases are not supported yet. Naming an indexed access over a union index through a separate alias is not handled today; keep the lookup on the concrete named keys.