Indexed access types (T[K])
Looks up the type of one or more properties on another type using bracket notation.
Type[Key]- Added in TypeScript 2.1
Parameters
| Parameter | Type | Description |
|---|---|---|
$Type | type | The object, array or tuple type being indexed. |
$Key | type | A type - a string or numeric literal, a union of them, number, or keyof Type. It must be a type, never a runtime variable. |
Return value
The type of the named property, or a union when Key is a unionThe type stored at Key. When Key is a union of keys the result is the union of the corresponding property types; Type[keyof Type] therefore yields the union of all value types.
Errors
Indexing with a key the type does not have is a compile-time error: Property 'alve' does not exist on type 'Person'.
Changelog
- PHP 2.1Introduced as "lookup types" together with keyof.
- PHP 4.0Variadic tuple types made T[number] on labelled and spread tuples far more useful for deriving parameter element types.
Indexed access reads a type the same way you read a property: Person["age"] is whatever type age holds. The index has to be a type, which is the rule that catches everybody — const key = "age"; type Age = Person[key] fails, because key is a value. Write type Key = "age" instead, or index with typeof key.
When to reach for it
To avoid exporting a type for every nested shape. If you already export ApiResponse, you do not need a separate ApiUser — ApiResponse["user"] is that type, and it stays correct when the response changes.
The high-value form is T[number] on arrays and tuples, which pulls out the element type. Combined with a type query and as const it turns a literal array into a union: (typeof ROUTES)[number].
What trips people up
A union index gives a union result, so Person[keyof Person] is every value type at once — useful, but not a way to narrow. And on a tuple, T[0] is the first element's type while T[number] is the union of all of them; reaching for number when you wanted a specific slot silently widens what you get.
Example
When to use it
- Derive a prop type from a component's props interface instead of exporting it separately
- Turn a const array of route names into a union with (typeof ROUTES)[number]
- Extract a nested payload type out of a large API response type
Frequently asked questions
How do I get the element type of an array type?
Index it with number: MyArray[number]. For an array value rather than a type, use (typeof myArray)[number], and add as const to the array if you want the elements as literal types instead of widened string or number.
Why does Person[key] fail when key is a const string?
Because the index position expects a type and a const is a value. TypeScript reports "'key' refers to a value, but is being used as a type". Write Person[typeof key], or declare type key = "age" and index with that.
How do I get a union of all the values in an object type?
Index by all of its keys: T[keyof T]. For a real object value the full form is (typeof obj)[keyof typeof obj], usually with as const on the object so the values stay literal.
More examples
Unions as the index
Each index distributes over the union it is given, so the result is the union of the matching property types. Person[keyof Person] is the idiom for "every value type in this object". The commented line shows that a typo is caught immediately rather than resolving to any.
type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];
// type Age = number
type I1 = Person["age" | "name"];
// type I1 = string | number
type I2 = Person[keyof Person];
// type I2 = string | number | boolean
type AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName];
// type I3 = string | boolean
// type Oops = Person["alve"];
// error: Property 'alve' does not exist on type 'Person'Arrays, tuples and the value-vs-type rule
On a tuple a numeric literal index picks one slot while number collapses to the union of every slot, which is the difference between First and Either. Chained accesses work exactly like chained property reads, so User["tags"][number] walks into the array and out again as string. The final block is the value-versus-type rule: a const is a value, and typeof is what lifts it into a position where indexing accepts it.
type Pair = [id: number, label: string];
type First = Pair[0]; // number
type Either = Pair[number]; // number | string
type Users = { id: number; tags: string[] }[];
type User = Users[number]; // { id: number; tags: string[] }
type Tag = User["tags"][number]; // string
const key = "id";
// type Bad = User[key];
// error: 'key' refers to a value, but is being used as a type
type Good = User[typeof key]; // number
type AlsoGood = User["id"]; // number
Discussion