Back to Blog
Lesson 23 of the TypeScript: Typing JavaScript with Confidence course
TypeScriptAugust 10, 20263 min read

Using keyof for Dynamic Access in TypeScript

Learn how to use the keyof operator to extract keys from your interfaces, enabling type-safe dynamic property access that prevents runtime typos.

TypeScriptType SafetyGenericskeyofInterfaces
Macro shot of an old brass key featuring worn text on a white surface.

Previously in this course, we explored constraining generics to build reusable components. In this lesson, we add the keyof operator to our toolkit, which allows us to bridge the gap between hardcoded object shapes and dynamic runtime data access.

The Problem: Fragile Dynamic Access

In JavaScript, accessing object properties dynamically is common. You might have a helper function that updates a specific field on a Task object, like so:

JAVASCRIPT
function updateTask(task, key, value) {
  task[key] = value; // Danger: TypeScript doesnCE9178">'t know if 'key' exists!
}

If you pass a key like "priorityLevel" but your Task interface only defines "priority", you create a silent bug. Even worse, if you mistype the string, the function fails silently at runtime. We need a way to tell TypeScript: "This string must be one of the keys of this object."

Using Keyof for Type Safety

The keyof operator takes an object type and produces a union of its literal keys. If you have an interface, keyof turns it into a "whitelist" of valid property names.

Let's apply this to our ongoing Task API client project.

TYPESCRIPT
interface Task {
  id: string;
  title: string;
  isCompleted: boolean;
}

// Resulting type: "id" | "title" | "isCompleted"
type TaskKey = keyof Task;

Now, we can constrain our updateTask function to only accept valid keys of the Task object.

Worked Example: A Type-Safe Setter

Let's refine our function signature using generics and the keyof operator. This ensures that the key argument is strictly tied to the properties of the provided object.

TYPESCRIPT
interface Task {
  id: string;
  title: string;
  isCompleted: boolean;
}

function updateTask<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
  obj[key] = value;
}

const myTask: Task = { id: "1", title: "Learn TS", isCompleted: false };

// Valid calls
updateTask(myTask, "title", "Mastering keyof"); // Success
updateTask(myTask, "isCompleted", true);       // Success

// Invalid calls(TypeScript will throw an error)
// updateTask(myTask, "description", "Oops"); // Error: CE9178">'description' is not a key of Task
// updateTask(myTask, "id", 123);              // Error: Argument 123 is not assignable to string

By using T[K], we are performing an indexed access type, which tells TypeScript that the value must match the type of the specific property found at key.

Hands-on Exercise

  1. Take your existing Task interface from previous lessons.
  2. Write a function called getTaskValue that takes a Task object and a key, and returns the value of that key.
  3. Ensure the return type of the function is correctly inferred based on the key provided.
  4. Try to pass a non-existent key to the function and observe the red squiggly line in your editor.

Common Pitfalls

  • Forgetting Generics: If you don't use a generic <T>, keyof will be tied to a specific interface, making your utility function non-reusable. Always use keyof T when writing generic utilities.
  • Assuming Keyof works on Instances: Remember that keyof operates on types (interfaces/aliases), not runtime objects. You must use keyof typeof myObject if you want to extract keys from a plain object variable without an interface.
  • Over-complicating: If you are only accessing one or two known properties, don't force a keyof abstraction. It is best used for generic utilities, filters, or form handlers.

FAQ

Can I use keyof with arrays? Yes. keyof string[] will include "length", "push", "pop", and other array methods.

What if I need to handle keys that don't exist yet? Check out our lesson on TypeScript index signatures to learn how to allow arbitrary dynamic keys while maintaining safety.

How does this relate to mapped types? keyof is the foundation of Mapped Types. You use keyof to iterate over the keys of an object to transform them, which we'll cover in Preventing Runtime Property Errors with TypeScript Mapped Types.

Recap

The keyof operator is your primary tool for enforcing constraints on dynamic property access. By combining keyof with generics, you create APIs that are self-documenting and resilient against typos.

Up next: We'll explore Indexed Access Types to extract specific property types from our interfaces.

Similar Posts