Skip to content
TypeScript ts advanced-types 4 min read

Indexed Access Types

Indexed access types — also called lookup types — let you read the type of a property by indexing into another type with T[K], mirroring the way you read a value with obj[key] at runtime. The key can be a single literal, a union of keys, the result of keyof, or number for array elements. This is the mechanism that makes generic getters return precise types and that powers nearly every utility type. This page covers single lookups, union lookups, array and tuple element access, and nested chains.

Looking up a single property

Index a type with a literal key to extract that property’s type. The bracket syntax always operates on types, never values.

interface User {
  id: number;
  name: string;
  address: { city: string; zip: string };
}

type Id = User["id"]; // number
type Name = User["name"]; // string

User["id"] resolves to number. Using a key that does not exist is an error, so the lookup is checked just like property access on a value.

// type Bad = User["age"]; // ❌ Error: Property "age" does not exist on type "User"

Union keys and keyof

Index with a union of keys and you get back a union of the corresponding value types. Indexing with keyof T therefore yields a union of every value type in the object.

type IdOrName = User["id" | "name"]; // number | string

type ValueTypes = User[keyof User];
// number | string | { city: string; zip: string }

This is extremely useful for deriving a union of all possible values in an object — for example turning a status-code map into a union of codes.

ExpressionResult
T["a"]type of property a
T["a" | "b"]union of a and b types
T[keyof T]union of all value types
T[number]element type of an array/tuple

Array and tuple element types

Index an array type with number to get its element type, and index a tuple with a numeric literal to get the type at that position. This avoids hard-coding element types separately from the array.

type StringArray = string[];
type Element = StringArray[number]; // string

const point = [10, 20, "label"] as const;
type X = (typeof point)[0]; // 10
type Label = (typeof point)[2]; // "label"
type Any = (typeof point)[number]; // 10 | 20 | "label"

StringArray[number] is string. For the readonly tuple, indexing by literal gives the exact position type, while [number] gives the union of all elements — a common way to derive a literal union from an as const array.

Indexing with number works on any array-like type. Combined with typeof and as const, (typeof arr)[number] is the idiomatic way to turn a value array into a type union.

Nested and chained lookups

Lookups chain just like property access, so you can drill into deeply nested shapes without re-declaring intermediate types.

type City = User["address"]["city"]; // string

interface Api {
  response: {
    data: { items: { sku: string; price: number }[] };
  };
}

type Item = Api["response"]["data"]["items"][number]; // { sku: string; price: number }
type Price = Api["response"]["data"]["items"][number]["price"]; // number

Each bracket steps one level deeper. The [number] step extracts the element type of the items array so you can then read price from a single element. These chains stay correct automatically as the source types evolve.

Powering generic getters

Indexed access is what gives generic property accessors their exact return types. The constraint K extends keyof T guarantees the key is valid, and the return type T[K] resolves to that key’s value type.

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

const users = [
  { id: 1, name: "Ada" },
  { id: 2, name: "Lin" },
];

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

T[K][] means “an array of the value type at key K.” Calling pluck(users, "id") returns number[], while "name" returns string[] — all from one signature, with invalid keys rejected at compile time.

Best Practices

  • Use T[K] to read property types instead of duplicating them in a separate alias.
  • Index with keyof T to derive a union of all of a type’s value types.
  • Use T[number] to extract array and tuple element types.
  • Combine (typeof arr)[number] with as const to build literal unions from value arrays.
  • Chain lookups (T["a"]["b"]) to reach nested types without intermediate aliases.
  • Return T[K] from generic getters so callers receive precise value types.
Last updated June 29, 2026
Was this helpful?