Recently, while working on a TypeScript feature that involved updating specific fields on an object, I ran into a problem. I wanted to allow dynamic property access, but only for specific fields, and TypeScript wasn’t providing the safety as required.
After searching for solutions, I found out that we can use the keyof operator along with some utility types to restrict which properties could be updated.
In this blog, I will explain about TypeScript’s keyOf operator, from basic usage to advanced patterns.
What is the keyof Operator?
The keyof operator in TypeScript is a type operator that takes an object type and produces a string or numeric literal union of its keys. Simply put, it gives you all the available property names of an object as a type.
For example, if you have a User interface with properties like name, age, and email, then keyof User would be equivalent to the type “name” | “age” | “email”.
Using keyof for Type-Safe Property Access
The most common use of keyof is to ensure type-safe property access when you’re dealing with dynamic property names. Here’s how I typically use it:
interface Person {
name: string;
age: number;
address: string;
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const john: Person = {
name: "John Smith",
age: 35,
address: "123 Main St, New York, NY"
};
// Type-safe property access
const name = getProperty(john, "name"); // Returns string
const age = getProperty(john, "age"); // Returns number
// This would cause a compile error
// const invalid = getProperty(john, "salary");Output:

In this example, the getProperty function is completely type-safe. The compiler knows that the key must be one of the keys of T, and it also knows the exact return type based on which key you provide.
Check out: Double Question Mark (??) Operator in TypeScript
keyof with Index Signatures
When working with objects that have index signatures, keyof behaves a bit differently:
interface Dictionary {
[key: string]: string;
}
// keyof Dictionary is just 'string'
type DictKeys = keyof Dictionary;
// This is valid since 'anyString' is a string
const dict: Dictionary = { hello: "world" };
const value = dict["anyString"]; // No error, but returns undefinedWith index signatures, keyof Returns the type of the index (usually string or number).
Combining keyof with Other Type Operators
Method 1: Using keyof with Mapped Types
One powerful way to use keyof is with mapped types to create new types based on existing ones:
interface User {
id: number;
name: string;
email: string;
}
// Create a type with all properties of User set to optional
type PartialUser = {
[K in keyof User]?: User[K]
};
// Create a type with all properties of User set to readonly
type ReadonlyUser = {
readonly [K in keyof User]: User[K]
};
// Create a type with only specific properties
type UserCredentials = {
[K in keyof User as K extends 'name' | 'email' ? K : never]: User[K]
};Output:

This pattern is so useful that TypeScript actually provides utility types like Partial<T>, Readonly<T>, and Pick<T, K> that use this exact approach.
Check out: Nullish Coalescing (??) vs Logical OR (||) Operator in TypeScript
Method 2: Using keyof with Conditional Types
Another powerful pattern is combining keyof with conditional types:
type ExtractStringProps<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K]
};
interface Product {
id: number;
name: string;
price: number;
description: string;
inStock: boolean;
}
// This will only include 'name' and 'description'
type ProductStringProps = ExtractStringProps<Product>;This allows you to filter types based on their property values, not just their property names.
Practical Applications of keyof
Type-Safe Event Handlers
When building a component system with TypeScript, keyof can help create type-safe event handlers:
interface Events {
click: { x: number; y: number };
focus: undefined;
input: { value: string };
}
function addEventListener<K extends keyof Events>(
event: K,
handler: (data: Events[K]) => void
) {
// Simulated event trigger
console.log(`Event listener registered for: ${event}`);
// For demonstration, manually call the handler with sample data
if (event === "click") {
handler({ x: 100, y: 200 } as Events[K]);
} else if (event === "focus") {
handler(undefined as Events[K]);
} else if (event === "input") {
handler({ value: "Hello, World!" } as Events[K]);
}
}
// TypeScript knows these are correct
addEventListener("click", (data) => {
console.log("Click event triggered:", data.x, data.y);
});
addEventListener("focus", () => {
console.log("Focus event triggered");
});
addEventListener("input", (data) => {
console.log("Input event triggered with value:", data.value);
});
// This would be a type error
// addEventListener("keydown", () => {});Output:

Creating Object Transformers
In data processing applications, I often need to transform objects:
interface ApiResponse {
userId: number;
userName: string;
userEmail: string;
}
interface NormalizedUser {
id: number;
name: string;
email: string;
}
// Map from source keys to target keys
type KeyMap<T, U> = {
[K in keyof T]: keyof U;
};
const apiToUserMap: KeyMap<ApiResponse, NormalizedUser> = {
userId: "id",
userName: "name",
userEmail: "email",
};
function transformObject<T, U>(
source: T,
target: new () => U,
keyMap: KeyMap<T, U>
): U {
const result = new target();
(Object.keys(source) as Array<keyof T>).forEach(key => {
const targetKey = keyMap[key];
(result as any)[targetKey] = source[key];
});
return result;
}This allows for type-safe object transformations, which is incredibly valuable when working with APIs.
Check out: Check If Object Is Undefined in TypeScript
Advanced keyof Patterns
Method 3: keyof with Generic Constraints
You can use keyof to constrain generics to specific property types:
function getNumericProperty<T, K extends keyof T>(
obj: T,
key: K,
// This constraint ensures we only accept keys where the value is a number
...args: T[K] extends number ? [] : never
): T[K] extends number ? T[K] : never {
console.log(`Accessing numeric property '${String(key)}':`, obj[key]);
return obj[key] as any;
}
interface Product {
id: number;
name: string;
price: number;
}
const product: Product = { id: 1, name: "Laptop", price: 999 };
// These work
const id = getNumericProperty(product, "id");
console.log("Product ID:", id);
const price = getNumericProperty(product, "price");
console.log("Product Price:", price);
// const name = getNumericProperty(product, "name");Output:

Check out: Mapped Types in TypeScript
Method 4: Using keyof with Classes
The keyof operator works great with class types, too:
class Employee {
id: number = 0;
firstName: string = "";
lastName: string = "";
getFullInfo() {
return `${this.firstName} ${this.lastName} (ID: ${this.id})`;
}
}
// Define a type that excludes method "getFullInfo"
type UpdatableEmployeeFields = Exclude<keyof Employee, "getFullInfo">;
// Only allow updating specific properties
function updateEmployee<K extends UpdatableEmployeeFields>(
employee: Employee,
field: K,
value: Employee[K]
) {
employee[field] = value;
console.log(`Updated ${String(field)} to:`, value);
}
const emp = new Employee();
updateEmployee(emp, "firstName", "Jane");
updateEmployee(emp, "lastName", "Doe");
updateEmployee(emp, "id", 101);
console.log("Full Info:", emp.getFullInfo());Output:

Check out: Ternary Operator in TypeScript
I hope you found this article helpful in understanding TypeScript’s keyof operator. This feature will save you a lot of time in debugging by ensuring type safety when working with dynamic property access. Whether you’re building complex data transformations, type-safe APIs, or just want better autocompletion, keyof is an essential tool in your TypeScript code.

Bijay Kumar is an experienced Python and AI professional who enjoys helping developers learn modern technologies through practical tutorials and examples. His expertise includes Python development, Machine Learning, Artificial Intelligence, automation, and data analysis using libraries like Pandas, NumPy, TensorFlow, Matplotlib, SciPy, and Scikit-Learn. At PythonGuides.com, he shares in-depth guides designed for both beginners and experienced developers. More about us.