TypeScript β€” Indexed Access Types: Complete Guide with Examples

Introduction – Why Use Indexed Access Types?

In TypeScript, Indexed Access Types allow you to reference the type of a specific property from another type. This powerful mechanism promotes type reusability, accuracy, and type safetyβ€”especially useful when working with complex objects, generics, or deeply nested types.

What You’ll Learn:

  • What indexed access types are
  • How to use them with keys, unions, arrays, and generics
  • Detailed explanations for every code snippet
  • Real-world usage patterns

What Are Indexed Access Types?

Indexed Access Types let you dynamically reference a type of a property using its key:

type PropertyType = TypeName[Key];
  • TypeName: any object type (e.g., interface, type alias)
  • Key: the name of a property in the object type

Example 1: Extracting a Property Type

type User = {
  name: string;
  age: number;
};

type NameType = User["name"]; // string
type AgeType = User["age"];   // number

Explanation:

  • User is a type with two properties: name (string) and age (number).
  • User["name"] accesses the type of the name property β€” which is string.
  • User["age"] accesses the type of the age property β€” which is number.

You don’t need to repeat the structure manually. This ensures automatic consistency with the User type.


Example 2: Using keyof with Indexed Access Types

type UserKeys = keyof User;         // "name" | "age"
type ValueTypes = User[UserKeys];   // string | number

Explanation:

  • keyof User returns a union of all keys: "name" | "age".
  • User[UserKeys] means we’re accessing both User["name"] and User["age"] β€” resulting in a union of their types: string | number.

This is how you extract all value types from an object type dynamically.


Example 3: Indexing with Union of Keys

type Person = {
  id: number;
  name: string;
  active: boolean;
};

type IdOrStatus = Person["id" | "active"]; // number | boolean

Explanation:

  • You’re accessing two properties at once: id (number) and active (boolean).
  • The result is a union: number | boolean.

This pattern is useful when extracting specific subsets of property types.


Example 4: Indexed Access with Arrays

type Roles = ["admin", "user", "guest"];
type Role = Roles[number]; // "admin" | "user" | "guest"

Explanation:

  • Roles is a tuple (fixed-length array) with three string literals.
  • Using Roles[number] means we’re asking: β€œWhat is the type of any element in Roles?”
  • Since it’s a union of all possible index values (0, 1, 2), the result is "admin" | "user" | "guest".

This is commonly used for defining value enums or dropdown options.


Example 5: Type-Safe Property Accessor Function

function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const product = { id: 101, name: "Notebook", price: 500 };
const id = getValue(product, "id");     // number
const name = getValue(product, "name"); // string

Explanation:

  • T is a generic type representing any object.
  • K extends keyof T ensures that key must be a valid property of T.
  • T[K] dynamically returns the type of that property.

This prevents typos and incorrect property access during development.


Example 6: Nested Indexed Access Types

type Profile = {
  user: {
    name: string;
    contact: {
      email: string;
      phone: string;
    };
  };
};

type ContactType = Profile["user"]["contact"]; // { email: string; phone: string }
type EmailType = Profile["user"]["contact"]["email"]; // string

Explanation:

  • You’re chaining property access to go deep inside nested objects.
  • Profile["user"] gives you the type of the user object.
  • Adding another layer: ["contact"], and then ["email"] gives the final string type.

This is particularly useful when working with API response types or configuration schemas.


Example 7: With Mapped Types & keyof

type Car = {
  brand: string;
  year: number;
};

type CarProperties = {
  [K in keyof Car]: Car[K];
};

Explanation:

  • This is a mapped type.
  • For each key in Car, we get its type via Car[K] using indexed access.
  • The result is effectively the same as the original Car type.

This pattern is how many built-in utility types (like Pick, Record, Partial) work.


Best Practices for Indexed Access Types

  • Use with generics: Great for reusable functions.
  • Avoid repetition: DRY your types by referencing them instead of rewriting.
  • Type safety: Reduces runtime errors from invalid property access.

Summary – Recap & Use Cases

Indexed access types are a foundational feature of TypeScript that allows developers to look up property types dynamically. Whether you’re working with generics, APIs, or arrays, this approach ensures better scalability and type integrity.

Key Takeaways:

  • Use Type["key"] to extract the type of a property
  • Combine with keyof to iterate through keys
  • Supports deep indexing and union keys
  • Widely used in utility types, schema validation, and component props

Real-World Scenarios:

  • Extracting data from deeply nested API responses
  • Validating form fields based on object structure
  • Creating reusable, safe dynamic functions

FAQs

What is the difference between typeof and indexed access types?

FeaturetypeofIndexed Access Type
PurposeExtracts type from a valueExtracts type from a property
Usagetypeof someValueSomeType["property"]
Exampletypeof userUser["name"]

Can indexed access types work with tuples and arrays?

Yes! Use numeric keys:

type MyTuple = [string, number];
type First = MyTuple[0]; // string
type Second = MyTuple[1]; // number

Do utility types in TypeScript use indexed access?

Absolutely! Built-in types like Pick<T, K> and Record<K, T> heavily rely on this feature under the hood.


Is it safe to use nested indexed types?

Yes, and it’s encouraged for API-heavy or config-heavy applications. Just ensure the path exists, or combine with optional chaining types (?. for runtime, Partial for types).


Share Now :
Share

TypeScript β€” Indexed Access Types

Or Copy Link

CONTENTS
Scroll to Top