keyof & Indexed Access
keyof gives you the keys as a type
Section titled “keyof gives you the keys as a type”The keyof operator takes an object type and produces a union of its keys as a type.
interface User { id: number; name: string; active: boolean;}
type UserKey = keyof User;// ^? "id" | "name" | "active"keyof User is not a value — it is the union type "id" | "name" | "active". This is the raw material for iterating over a type, which is exactly what mapped types do.
For an object type with an index signature, keyof gives the index type:
type Dict = { [key: string]: number };type DictKey = keyof Dict;// ^? string | number (JS object keys can be accessed by number too)Indexed access: T[K] looks up value types
Section titled “Indexed access: T[K] looks up value types”Once you have keys, indexed access looks up the type of the value at a key — the type-level version of obj[key].
interface User { id: number; name: string;}
type IdType = User["id"];// ^? number
type NameType = User["name"];// ^? stringYou can index with a union of keys to get a union of value types:
type Values = User[keyof User];// ^? number | stringUser[keyof User] reads as “the type of every value in User” — a common idiom for getting the union of a type’s value types.
flowchart LR
t["User { id: number, name: string }"] -->|keyof| keys["keys: id or name"]
keys -->|"T[key]"| vals["value types: number or string"] Indexing arrays and tuples
Section titled “Indexing arrays and tuples”Arrays and tuples are object types too, so indexed access works on them — and [number] is the trick for “the element type”:
type Nums = number[];type Elem = Nums[number];// ^? number
const tuple = [1, "two", true] as const;type TupleElem = (typeof tuple)[number];// ^? 1 | "two" | true
type First = (typeof tuple)[0];// ^? 1Arr[number] means “index this array with any number,” which yields the union of all element types — the standard way to turn an array/tuple type into a union.
Why these two matter so much
Section titled “Why these two matter so much”Nearly every utility type is built from keyof plus indexed access plus a mapped type:
// Pick, from scratch — iterate chosen keys, look up each value typetype MyPick<T, K extends keyof T> = { [P in K]: T[P];};
type NameOnly = MyPick<User, "name">;// ^? { name: string }K extends keyof T constrains the keys to real keys of T; [P in K] iterates them (a mapped type, next lesson if you skipped it); T[P] looks up each value type. Three primitives, one useful tool.