Keyof & Indexed Access
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
stringas a key type instead ofkeyof T, which removes safety. - Assuming
keyofalways 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 Tfor dynamic property access utilities. - Use indexed access (
T[K]) to preserve correct value types. - Derive types from config objects using
typeofto 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.