TypeScript

keyof in TypeScript

keyof takes a type and returns a union of its property names. That's it. One operator, but it unlocks some of the most useful patterns in TypeScript.

23 Apr 2024

keyof in TypeScript

keyof takes a type and returns a union of its property names. That's it. One operator, but it unlocks some of the most useful patterns in TypeScript.

Typescript
interface User {
  name: string;
  age: number;
  email: string;
}

type UserKeys = keyof User; // "name" | "age" | "email"

Why this matters

The real power shows up in generic functions. Say you want a function that reads a property from any object, but you want TypeScript to know the return type:

Typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user: User = { name: "Ehsan", age: 30, email: "ehsan@gazar.dev" };

const name = getProperty(user, "name"); // TypeScript knows this is a string
const age = getProperty(user, "age");   // TypeScript knows this is a number
getProperty(user, "foo");               // Compile error — "foo" is not a key of User

Without keyof, you'd use string as the key type and lose all type information about the return value.

Constraining function parameters

Typescript
function processUser(user: { [key in keyof User]: any }) {}

This ensures the function only accepts objects that have exactly the same keys as User, even if the value types differ.

The trade-off

keyof makes your types more precise. That's the benefit — fewer runtime surprises, better autocomplete, safer refactoring.

The cost: your type signatures become harder to read. Generic functions with multiple keyof constraints can look intimidating. If your teammates aren't comfortable with advanced TypeScript, simpler (less type-safe) approaches might be the better choice for your team.

I use keyof heavily in utility types and library code. In application code, I keep it simple unless type safety demands otherwise.