Keyof & Indexed Access

Learn how keyof and indexed access types help you build type-safe property access and reusable utilities. These patterns are essential for scalable TypeScript APIs and refactor-safe code.

On this page

Why keyof and indexed access matter

In real applications, you often need to access object properties dynamically: building generic helpers, mapping over keys, validating payloads, or creating reusable UI components. Without types, dynamic access becomes unsafe and refactors become risky. TypeScript provides keyof and indexed access types to make dynamic property access type-safe.

What is keyof?

keyof produces a union of the property names (keys) of a given object type. This lets TypeScript treat keys as a constrained set of valid values.

type User = {
  id: number;
  name: string;
  email: string;
};

type UserKey = keyof User;
// "id" | "name" | "email"

Why this is powerful

Once you have a union of valid keys, you can use it to prevent typos and invalid property access. This is especially valuable when building reusable utilities and shared libraries.

Type-safe getProperty

A common pattern is a type-safe property getter. The key idea is: the key must be one of the object’s keys.

function getProperty(obj: T, key: K) {
  return obj[key];
}

const user = { id: 1, name: "Ozan", email: "ozan@example.com" };

getProperty(user, "name");  // OK
// getProperty(user, "age"); // Error

Indexed access types

Indexed access types allow you to look up the type of a property by its key. Use the syntax T[K].

type User = { id: number; name: string; email: string };

type NameType = User["name"]; // string
type IdType = User["id"];     // number

Combining keyof with indexed access

The real power appears when you combine these features. You can express relationships like: “if the key is K, then the value type is T[K]”.

function setProperty(obj: T, key: K, value: T[K]) {
  obj[key] = value;
}

const u = { id: 1, name: "Yusuf", email: "yusuf@example.com" };

setProperty(u, "name", "Elif"); // OK
// setProperty(u, "name", 123); // Error

Key unions from real objects

When you use keyof on a type, you get a union. You can also derive types from values using typeof.

const routes = {
  home: "/",
  docs: "/docs",
  pricing: "/pricing"
};

type RouteKey = keyof typeof routes;
// "home" | "docs" | "pricing"

type RoutePath = (typeof routes)[RouteKey];
// "/" | "/docs" | "/pricing"

This is a production-friendly approach for strongly typed configs and maps.

Using keyof for safe mapping

You can use keyof to build safe mapping helpers and avoid drifting schemas.

type User = { id: number; name: string; email: string };

type UserLabels = Record;

const labels: UserLabels = {
  id: "ID",
  name: "Name",
  email: "Email"
};

Common pitfalls

  • Using string as a key type instead of keyof T, which removes safety.
  • Assuming keyof always returns only string keys. Keys can also be number or symbol in some cases.
  • Overcomplicating generics where a simple explicit type would be clearer.

Production guidance

  • Prefer K extends keyof T for dynamic property access utilities.
  • Use indexed access (T[K]) to preserve correct value types.
  • Derive types from config objects using typeof to keep values and types in sync.
  • Keep helper APIs small and predictable; generic power is not a reason to build complex abstractions.

What’s next

Now that you can express type-safe dynamic access, the next step is learning literal types, which enable extremely precise domain modeling and discriminated unions.