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.

5 min read

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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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:

typescripttypescript
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:

texttext
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:

typescripttypescript
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:

typescripttypescript
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

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.
RunePowered by Rune AI

Frequently Asked Questions

Can I use a runtime variable as the index in an indexed access type?

No. The index must be a type, not a value. If you have a const variable and want to use it, convert it to a type first with typeof: type Age = Person[typeof key].

What happens if I index with a property that does not exist?

TypeScript reports a compiler error. The error message says the property does not exist on the type, just like accessing a nonexistent property on an object value.

How do indexed access types interact with optional properties?

Indexing an optional property includes undefined in the result type. For example, if age is optional on Person, then Person["age"] is number | undefined.

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.