Indexed Access Types in TypeScript
Indexed access types look up the type of a specific property from another type. Learn the syntax, how to use unions and keyof as index types, and practical patterns for type-safe property access.
TypeScript indexed access types look up the type of a specific property from another type. You write one with bracket notation, similar to accessing an object property at runtime, but the index is a type instead of a value.
Here is the basic syntax:
type Person = {
name: string;
age: number;
email: string;
};
type PersonAge = Person["age"];PersonAge is number. TypeScript looks at the Person type, finds the age property, and extracts its type. The index "age" is a string literal type, not a runtime string.
If you try to index a property that does not exist, TypeScript catches the mistake at compile time:
type Bad = Person["address"];This produces an error: Property 'address' does not exist on type 'Person'. The check is purely at the type level, so you get the error before running any code.
Using Unions as the Index
A single string literal is not the only valid index. The index type can also be a union of string literals, which extracts multiple property types from the object at once:
type NameOrAge = Person["name" | "age"];NameOrAge is string | number. TypeScript looks up both properties and unions their types together. This is useful when a function might work with any of several fields from an object type.
You can also use a type alias for the index to keep the code readable:
type StringFields = "name" | "email";
type StringValues = Person[StringFields];StringValues is string because both name and email are strings. TypeScript simplifies the union of identical types.
Using keyof as the Index
Combining indexed access types with keyof lets you extract all property types from an object type:
type AllPersonValues = Person[keyof Person];AllPersonValues is string | number. The keyof operator produces "name" | "age" | "email", and the indexed access looks up all three. The result is the union of all property types.
This pattern is common in generic functions that need to return the type of any property from a given object:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}The return type T[K] is an indexed access type. It means "the type of the property K on the object T," and TypeScript infers the exact return type based on the key argument.
This pattern is the foundation for many type-safe utility functions that work with object properties generically. For more on how this works with generics, see generic constraints in TypeScript.
Extracting Array Element Types
A common and useful pattern is using number as the index to get the element type of an array:
const users = [
{ name: "Ada", age: 30 },
{ name: "Bob", age: 25 },
];
type User = typeof users[number];First, typeof users captures the array type. Then, indexing with number extracts the element type, so the resulting User type is { name: string; age: number }.
You get the element type without manually writing an interface that mirrors the array data.
You can chain indexed accesses to drill deeper:
type UserAge = typeof users[number]["age"];UserAge is number. This drills from the array to the element to the age property. For more on typeof, see typeof type operator in TypeScript.
The Index Must Be a Type
The most common beginner mistake is using a runtime value as the index. This code fails because key is a value, not a type:
const key = "age";
type Age = Person[key];The index position in bracket notation requires a type, and a const variable is a runtime value even though it has a literal type. TypeScript rejects it with this error:
Type 'key' cannot be used as an index type.
'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?The fix is to give the index its own named type instead of a const variable, so TypeScript treats it as a type from the start:
type KeyType = "age";
type Age = Person[KeyType];If you must reference the existing const variable instead of writing a new type alias, convert it with typeof in the index position:
const key = "age";
type Age = Person[typeof key];typeof key evaluates to the literal type "age", which is a valid index type. This works because const declarations have literal types in TypeScript.
For practical patterns that combine these operators into useful, reusable utilities you can drop into real TypeScript projects right away, see build a type safe object keys helper. Indexed access types are the bridge between object shapes and the specific property types your functions need to work with.
Rune AI
Key Insights
- Indexed access types use bracket notation to look up a property type: Type[Key].
- The index must be a type, not a runtime value.
- Use a union of string literals as the index to extract multiple property types at once.
- Use number as the index on an array type to get the element type.
- Combine with keyof for type-safe generic property access.
Frequently Asked Questions
Can I use a runtime variable as the index in an indexed access type?
What happens if I index with a property that does not exist?
How do indexed access types interact with optional properties?
Conclusion
Indexed access types let you extract a specific property type from a larger type using bracket notation. The index itself is a type, which means you can use unions, keyof, and number to express flexible lookups. This operator is the foundation for many generic patterns, from type-safe property accessors to deriving element types from arrays.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.