Suggest an editImprove this articleRefine the answer for “How keyof and typeof work in TypeScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**keyof** extracts a type's property names as a string literal union. **typeof** captures the type of any value at compile time. ```typescript const user = { name: "Bob", age: 30 }; type User = typeof user; // { name: string; age: number } type UserKey = keyof typeof user; // "name" | "age" ``` **Key point:** `keyof` works on types to list their keys; `typeof` works on values to infer their type. Combined, they let you derive a key union directly from a runtime value.Shown above the full answer for quick recall.Answer (EN)Image**keyof** extracts a type's property names as a string literal union. **typeof** captures the type of any value at compile time, so you can reference that type without writing it twice. ## Theory ### TL;DR - `keyof Type` returns a union of that type's key names: `keyof Person` gives `"name" | "age"` - `typeof value` returns the inferred type of a value: `typeof person` gives `{ name: string; age: number }` - Think of `keyof` as the button panel on a vending machine: it lists valid choices, nothing more. `typeof` is reading the label on what you already hold - Use `K extends keyof T` to constrain a function parameter to valid keys; use `typeof obj` to avoid re-declaring an existing shape - Combined as `keyof typeof obj`, they give you a key union straight from a value, no separate type declaration needed ### Quick example ```typescript type Person = { name: string; age: number; }; type Keys = keyof Person; // "name" | "age" const person = { name: "Alice", age: 30 }; type PersonType = typeof person; // { name: string; age: number } const key: Keys = "name"; // OK // const bad: Keys = "email"; // Error: not assignable to "name" | "age" ``` `keyof` limits `key` to Person's actual properties. `typeof` copies `person`'s shape without you writing it out manually. ### Key difference `keyof` operates on types (interfaces, type aliases, class declarations) and produces a union of their property names. `typeof` operates on values (variables, expressions, function references) and produces a type you can use anywhere a type is expected. They solve opposite problems: `typeof` answers "what type does this value have?", `keyof` answers "what keys does this type expose?". Put them together and you go from a plain object in your code to a safe key union in a single step. ### When to use - Generic property accessor: `K extends keyof T` so that `obj[key]` is always type-safe and the return type is exact - Avoid re-declaring a type for an existing object or constant: `type Config = typeof config` - Iterate or map over an object's keys: `keyof` inside mapped types like `{ [K in keyof T]: ... }` - Extract prop types from a component or function: `Parameters<typeof Button>[0]` - Constrain a string argument to valid property names: `(key: keyof User) => User[key]` ### Comparison | | `keyof` | `typeof` | |---|---|---| | Input | A type | A value or expression | | Output | String literal union of property names | The inferred type of the value | | Works on | Interfaces, type aliases, classes | Variables, objects, functions, expressions | | Runtime cost | None | None | | Common combo | `T[K]` indexed access | `keyof typeof obj` | | Typical use | Constrain key parameters | Reuse existing object shapes | ### How the compiler handles this Both operators are compile-time only. No code is emitted for them at runtime. When TypeScript processes `keyof T`, it walks the type's property map and builds a string literal union from the public property names. For `typeof expr`, the type checker resolves the expression using control flow analysis and freezes the result as a type alias. One thing that catches developers: `let` widens the inferred type. `const y = 5` gives `typeof y` as `5` (a literal type). `let y = 5` gives `number`. TypeScript 4.1+ also includes symbol keys in `keyof` results, so the union can include `string | number | symbol` depending on the type. ### Common mistakes **Running `keyof` on a primitive** ```typescript type S = keyof string; // number | typeof Symbol.iterator | "toString" | "charAt" | ... // Gives you all string prototype members, not what you usually want type OK = keyof { x: string }; // "x" ``` Apply `keyof` to your own type definitions, not to built-in primitives. **`typeof` on an uninitialized variable** ```typescript let x; type T = typeof x; // any - no useful type information const y = 5; type U = typeof y; // 5 (literal type, much more useful) ``` Initialize with `const` before using `typeof` on a variable. **`keyof typeof` on an empty object** ```typescript let obj = {}; type K = keyof typeof obj; // string | number | symbol const typed: { a: number } = { a: 1 }; type K2 = keyof typeof typed; // "a" ``` An empty `{}` carries an implicit index signature in TypeScript. `keyof {}` resolves to the full index signature type, not `never`. Use `const` and provide explicit properties, or annotate the variable. **Forgetting symbol keys in TypeScript 4.1+** ```typescript // If you want only string keys from keyof: type StringKeys = Extract<keyof T, string>; ``` Before 4.1, `keyof` returned string keys only. From 4.1 onward, symbol keys are included too. ### Real-world usage - Zod: `type Schema = z.infer<typeof userSchema>` to turn a runtime validator into a TypeScript type - React: `type ButtonProps = Parameters<typeof Button>[0]` for component prop types without manual declaration - Redux Toolkit: `type State = ReturnType<typeof store.getState>` - Generic utilities: `function get<T, K extends keyof T>(obj: T, key: K): T[K]` is the standard safe accessor pattern - Mapped types: `Record<keyof Config, string>` to transform all keys of an existing type ### Follow-up questions **Q:** What does `keyof T[K]` produce? **A:** The keys of whatever type `T[K]` resolves to. If `T = { user: { name: string } }` and `K = "user"`, then `keyof T[K]` is `"name"`. **Q:** How does `keyof` behave on intersection types? **A:** `keyof (A & B)` gives `keyof A | keyof B`. An intersection type has all properties from both sides, so `keyof` collects all of them. **Q:** Does `typeof` work on overloaded functions? **A:** It captures the full overloaded call signature. To extract specific parts, use `Parameters<typeof fn>` or `ReturnType<typeof fn>`. **Q:** What changed in TypeScript 4.1 for `keyof`? **A:** Before 4.1, `keyof` returned only string keys. From 4.1 onward, it includes symbol keys as well, so the result type can be `string | number | symbol`. **Q (senior):** A developer wrote `keyof typeof obj` where `obj = {}` is declared with `let`. They expected `never` but got `string | number | symbol`. Why? **A:** Empty object `{}` in TypeScript has an implicit index signature that accepts any string, number, or symbol key. So `keyof typeof {}` resolves to that full index signature type. To get `never`, you need a type with no properties, such as `Record<never, never>`, or an explicitly annotated variable with a closed type. ## Examples ### Basic: type-safe property getter ```typescript type User = { id: number; name: string }; function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user: User = { id: 1, name: "Bob" }; const name = getProp(user, "name"); // inferred as string // getProp(user, "email"); // Error: "email" is not in User console.log(name); // "Bob" ``` `K extends keyof T` tells TypeScript that `key` must be an actual property of `obj`. The return type `T[K]` then resolves to the exact type for that property. No type assertion, no `any`, no runtime guard needed. ### Intermediate: config object with inferred types ```typescript const config = { host: "localhost", port: 3000, debug: true, } as const; type Config = typeof config; // { readonly host: "localhost"; readonly port: 3000; readonly debug: true } type ConfigKey = keyof Config; // "host" | "port" | "debug" function readConfig(key: ConfigKey): Config[typeof key] { return config[key]; } readConfig("host"); // "localhost" // readConfig("url"); // Error: "url" is not assignable to ConfigKey ``` `as const` preserves literal types instead of widening them. `typeof config` captures the exact shape, `keyof` then constrains which keys are valid. I've seen this pattern catch typos in config lookups that would otherwise surface only at runtime. ### Advanced: type-safe event system ```typescript const handlers = { userCreated: (id: number) => void 0, userDeleted: (id: number) => void 0, pageLoaded: () => void 0, }; type Handlers = typeof handlers; type EventName = keyof Handlers; // "userCreated" | "userDeleted" | "pageLoaded" function emit<K extends EventName>(name: K, ...args: Parameters<Handlers[K]>): void { (handlers[name] as (...a: unknown[]) => void)(...args); } emit("userCreated", 42); // OK // emit("userCreated"); // Error: expected 1 argument // emit("unknown", 42); // Error: "unknown" not in EventName ``` `keyof typeof handlers` gives the event name union. `Parameters<Handlers[K]>` then extracts the argument list for that specific handler. Both the event name and its arguments are fully type-checked at compile time.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.