The keyof Operator
The keyof operator takes an object type and produces a union of its keys as string (or number/symbol) literal types. It is the foundation of type-safe property access in TypeScript: instead of accepting any string, you can constrain a value to only the keys that actually exist on a type. Combined with generics and indexed access, keyof powers helpers like Object.keys, property getters, and most of the standard utility types. This page covers the basic operator, how it behaves with index signatures, and the canonical type-safe getter pattern.
Basic usage
Apply keyof to any object type and you get back a union of its property names as literal types. The result updates automatically when the source type changes, so it never drifts out of sync.
interface User {
id: number;
name: string;
email: string;
}
type UserKey = keyof User; // "id" | "name" | "email"
const key: UserKey = "name"; // ✅
// const bad: UserKey = "age"; // ❌ Error: not assignable to "id" | "name" | "email"
UserKey is exactly the three keys of User. Add a property to the interface and the union widens with no extra work, which is why keyof is preferred over hand-written string unions that you have to maintain by hand.
keyof with index signatures
When a type uses an index signature, keyof returns the index signature’s key type rather than a finite union. A string index signature yields string | number (because numeric keys are coerced to strings at runtime), and a number index signature yields number.
interface StringMap {
[key: string]: unknown;
}
type StringMapKeys = keyof StringMap; // string | number
interface NumberMap {
[key: number]: unknown;
}
type NumberMapKeys = keyof NumberMap; // number
| Source type | keyof result |
|---|---|
{ a: 1; b: 2 } | "a" | "b" |
{ [k: string]: V } | string | number |
{ [k: number]: V } | number |
string (primitive) | union of String.prototype members |
any | string | number | symbol |
The type-safe getter pattern
The most common use of keyof is constraining a generic parameter to the keys of another type. This makes a generic getProperty function reject invalid keys and infer the precise return type via indexed access.
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { id: 1, name: "Ada", active: true };
const name = getProperty(user, "name"); // string
const active = getProperty(user, "active"); // boolean
// getProperty(user, "missing"); // ❌ Error: "missing" is not a key of user
Because K extends keyof T, only real keys are allowed, and the return type T[K] is the exact type of that property — string for "name", boolean for "active". This single signature replaces dozens of overloads.
keyofoperates on types, not values. To get the keys of a runtime object you combine it withtypeof:keyof typeof someObject.
keyof on a value via typeof
You often have a concrete object and want its keys as a type. Use typeof to lift the value into a type, then apply keyof to that.
const config = {
host: "localhost",
port: 5432,
ssl: false,
} as const;
type ConfigKey = keyof typeof config; // "host" | "port" | "ssl"
function readConfig(key: ConfigKey) {
return config[key];
}
Here typeof config produces the object type, and keyof extracts its keys. The as const is optional but keeps the value’s literal types intact for the property values.
keyof with mapped types
keyof is the engine behind mapped types, which iterate in keyof T to transform every property. This is how Partial, Readonly, and friends are defined.
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
type NullableUser = Nullable<User>;
// { id: number | null; name: string | null; email: string | null }
The [K in keyof T] clause walks each key K, and T[K] | null widens each value to allow null. Every property is preserved with its original key.
Best Practices
- Derive key unions with
keyofinstead of duplicating string literals by hand. - Constrain generic key parameters with
K extends keyof Tfor compile-time safety. - Return
T[K](indexed access) from generic getters to keep precise value types. - Remember that
keyofon astringindex signature includesnumberas well asstring. - Use
keyof typeof valueto derive key types from concrete runtime objects. - Pair
keyofwithas constwhen you need literal key and value types preserved.