Mastering TypeScript's keyof: Type-Safe Dynamic Properties
TypeScriptAdvancedType Safety

Mastering TypeScript's keyof: Type-Safe Dynamic Properties

January 23, 2025
10 min read
Salman Izhar

Mastering TypeScript's keyof Keyword

The keyof operator is essential for type-safe TypeScript code. Let's explore how it works.

What is keyof?

keyof produces a union of the keys of an object type.
interface User {
  id: number;
  name: string;
  email: string;
}

type UserKeys = keyof User;
// Result: "id" | "name" | "email"

Basic Usage Example

function getProperty(obj: T, key: K): T[K] {
  return obj[key];
}

const product = {
  id: "1",
  title: "Laptop",
  price: 999
};

const title = getProperty(product, "title");  // Type: string
const price = getProperty(product, "price");  // Type: number

// ❌ Error
const invalid = getProperty(product, "invalid");

Real-World Example 1: Type-Safe Form Handler

interface FormData {
  username: string;
  email: string;
  age: number;
}

type FormErrors = {
  [K in keyof FormData]?: string;
};

class FormValidator {
  private errors: FormErrors = {};

  setError(field: K, message: string) {
    this.errors[field] = message;
  }

  getError(field: K) {
    return this.errors[field];
  }
}

// Usage
const validator = new FormValidator();
validator.setError("username", "Too short"); // Type-safe!

Real-World Example 2: Event Emitter

interface Events {
  login: { userId: string; timestamp: number };
  logout: { userId: string };
  error: { message: string; code: number };
}

class TypeSafeEventEmitter {
  private listeners: {
    [K in keyof Events]?: Array<(data: Events[K]) => void>;
  } = {};

  on(
    event: K,
    callback: (data: Events[K]) => void
  ) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event]!.push(callback);
  }

  emit(event: K, data: Events[K]) {
    this.listeners[event]?.forEach(cb => cb(data));
  }
}

// Usage
const emitter = new TypeSafeEventEmitter();
emitter.on("login", (data) => {
  // data is typed: { userId: string; timestamp: number }
  console.log(`User ${data.userId} logged in`);
});

Advanced: Mapped Types with keyof

// Make all properties optional
type Partial = {
  [K in keyof T]?: T[K];
};

// Make all readonly
type Readonly = {
  readonly [K in keyof T]: T[K];
};

// Pick specific properties
type Pick = {
  [P in K]: T[P];
};

Best Practices

1. Use with generics for flexibility 2. Constrain type parameters with K extends keyof T 3. Leverage index access T[K] 4. Document complex types

Conclusion

The keyof operator enables:

  • Type-safe property access
  • Dynamic key handling
  • Better IDE autocomplete
  • Fewer runtime errors

Master keyof for robust TypeScript!

Get More Like This

Want articles like this in your inbox?

Join developers and founders who get practical insights on frontend, SaaS, and building better products.

S

Written by Salman Izhar

Frontend Developer specializing in React, Next.js, and building high-converting web applications.

Learn More