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.

I am Bijay Kumar, a Microsoft MVP in SharePoint. Apart from SharePoint, I started working on Python, Machine learning, and artificial intelligence for the last 5 years. During this time I got expertise in various Python libraries also like Tkinter, Pandas, NumPy, Turtle, Django, Matplotlib, Tensorflow, Scipy, Scikit-Learn, etc… for various clients in the United States, Canada, the United Kingdom, Australia, New Zealand, etc. Check out my profile.