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.
| Expression | Result |
|---|---|
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
numberworks on any array-like type. Combined withtypeofandas 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 Tto 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]withas constto 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.