🔷 TypeScript Q38 / 101

What is keyof typeof pattern?

AI-Powered Answer ✓ Answered

The 'keyof typeof' pattern in TypeScript is a powerful combination of two distinct operators that allows you to extract a union of string literal types representing the keys of an object's *value* rather than its *type*. This is particularly useful for working with objects defined at runtime or declared as `const`.

The `typeof` Operator

In TypeScript, the typeof operator can be used in a type context to obtain the type of a variable or property. While in JavaScript, typeof returns a string indicating the primitive type (e.g., 'string', 'number'), in TypeScript, it infers the exact type, including literal types or object types.

typescript
const myString = "hello";
type MyStringType = typeof myString; // Type is '"hello"'

const myObject = {
  name: "Alice",
  age: 30
};
type MyObjectType = typeof myObject; 
// Type is { name: string; age: number; }

The `keyof` Operator

The keyof operator takes an object type and produces a union type of its known, enumerable string or symbol literal keys. It's used to get the keys from an *existing type*.

typescript
interface User {
  id: number;
  name: string;
  email?: string;
}

type UserKeys = keyof User; // Type is 'id' | 'name' | 'email'

const person = { firstName: "John", lastName: "Doe" };
type PersonKeys = keyof typeof person; // Type is 'firstName' | 'lastName'

Combining `keyof` and `typeof`

The keyof typeof pattern is used when you have an *object value* (rather than a defined type/interface) and you want to extract a union type of its keys. The typeof operator first extracts the type of the object value, and then keyof extracts the union of its keys from that inferred type.

typescript
const CONFIG = {
  API_URL: "https://api.example.com",
  TIMEOUT_MS: 5000,
  LOG_LEVEL: "info"
} as const; // Using 'as const' is crucial here for literal types

type ConfigKeys = keyof typeof CONFIG; 
// Type is 'API_URL' | 'TIMEOUT_MS' | 'LOG_LEVEL'

function getConfigValue<K extends ConfigKeys>(key: K): (typeof CONFIG)[K] {
  return CONFIG[key];
}

let apiUrl = getConfigValue("API_URL");   // Type is 'https://api.example.com'
let timeout = getConfigValue("TIMEOUT_MS"); // Type is 5000

Without as const, typeof CONFIG would infer { API_URL: string; TIMEOUT_MS: number; LOG_LEVEL: string; }, and ConfigKeys would then be string | number (from keyof typeof CONFIG). The as const assertion tells TypeScript to infer the narrowest possible literal types for the object's properties, allowing keyof typeof to produce a precise union of string literals for the keys.

Common Use Cases

  • Type-safe object indexing: Ensures you only access properties that actually exist on an object.
  • Iterating over object keys: Provides type safety when looping through keys, especially when the object's keys are known and finite.
  • Creating utility functions: For functions that operate on properties of a specific object, keyof typeof can enforce type correctness for the property names passed as arguments.
  • Defining string literal unions from runtime values: Useful when an enumeration of string values is maintained as a plain JavaScript object.
typescript
enum Status { 
  Pending = "PENDING", 
  Approved = "APPROVED", 
  Rejected = "REJECTED" 
}

// Using an object as a source for literal types
const STATUS_MESSAGES = {
  [Status.Pending]: "Your request is awaiting review.",
  [Status.Approved]: "Your request has been approved!",
  [Status.Rejected]: "Your request was rejected."
} as const;

type ValidStatus = keyof typeof STATUS_MESSAGES; // Type: 'PENDING' | 'APPROVED' | 'REJECTED'

function displayMessage(status: ValidStatus) {
  console.log(STATUS_MESSAGES[status]);
}

displayMessage(Status.Approved); // Output: "Your request has been approved!"
displayMessage("INVALID_STATUS"); // Type Error: Argument of type '"INVALID_STATUS"' is not assignable to parameter of type 'ValidStatus'.