Menu

TypeScript keyof: Get Object Keys as a Type, with Examples

keyof takes an object type and gives you the union of its property names. Learn keyof with interfaces, keyof typeof for plain objects, typed property access with generics, index signatures (string | number), and why Object.keys returns string[].

This page includes runnable editors - edit, run, and see output instantly.

keyof takes an object type and produces the union of its property names. keyof User below is the type "id" | "name" | "email", so a variable of that type can only hold one of those three strings.

keyof exists only in the type system. It is erased when the code compiles, so it never checks anything at runtime. What it gives you is a compile-time guarantee that a key names a real property: rename email in the interface and every "email" typed as keyof User becomes an error.

keyof typeof: Keys of a Plain Object

keyof works on types, not values. For an object you wrote as a value, first get its type with typeof, then take the keys:

Read it from the inside out: typeof colors is { red: string; green: string; blue: string }, and keyof of that is the key union. Writing keyof colors without typeof fails with error TS2749: 'colors' refers to a value, but is being used as a type here. Did you mean 'typeof colors'?.

This is the usual way to derive a union from a lookup table: add a color to the object and the Color type grows with it. typeof has a second, runtime meaning too; the typeof page covers both.

Typed Property Access with Generics

The most common use of keyof is a generic constraint. K extends keyof T says "K is one of the keys of T", and T[K] is the type of the property at that key:

The second type parameter matters. With key: keyof T and no K, the return type is T[keyof T], the union of every property type (string | number | boolean here), so title.toUpperCase() would fail with error TS2339: Property 'toUpperCase' does not exist on type 'string | number | boolean'. Capturing the exact key in K keeps the exact property type. T[K] is an indexed access type, described on the indexed access types page.

Indexing with a Plain string

A very common error appears when you index an object with a string that came from somewhere else:

The compiler prints:

index.ts(10,10): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Settings'.
  No index signature with a parameter of type 'string' was found on type 'Settings'.

Any string could reach read, including "colour", and Settings says nothing about that key. Two fixes: accept only real keys, or check an unknown string before using it.

name is keyof Settings is a type predicate: when the function returns true, TypeScript treats input as a key inside the if. The type guards page explains predicates in detail.

keyof with Index Signatures: string | number

For a type with an index signature, keyof returns the index type, and a string index signature gives string | number, not just string:

JavaScript converts every numeric property key to a string, so scores[42] and scores["42"] are the same property. TypeScript models that by including number. The output also shows a JavaScript rule: integer-like keys are listed first, in ascending order, before the other string keys. When a generic function needs string keys only, write Extract<keyof T, string> or string & keyof T; Exclude and Extract have their own page.

keyof Results at a Glance

Typekeyof gives
{ id: number; name: string }"id" | "name"
{ [key: string]: number }string | number
{ [index: number]: string }number
{}never
anystring | number | symbol
{ a: 1; b: 2 } | { a: 3; c: 4 }"a" (keys every member has)
{ a: 1 } & { c: 4 }"a" | "c" (keys of either)
string[]number plus every array method name ("length", "push", ...)

The union and intersection rows look backwards at first. A value of a union type might be either member, so only keys present in both are safe. A value of an intersection has everything from both, so it has all the keys.

Why Object.keys Returns string[]

Object.keys(obj) is typed as string[], never (keyof T)[]. The reason is structural typing: a value can carry more properties than its declared type.

p is typed Point, yet it has a z key at runtime. If Object.keys promised ("x" | "y")[], code relying on that would be wrong for p. The cast is safe for object literals you created yourself; for objects that arrive from outside, keep the keys as string and check them.

keyof in Mapped Types

keyof is also how you loop over the keys of a type to build a new type. This is how the built-in Partial and Readonly utility types are written:

type MyPartial<T> = { [K in keyof T]?: T[K] };
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };

interface User {
  id: number;
  name: string;
}

type UserDraft = MyPartial<User>; // { id?: number; name?: string }

[K in keyof T] visits each key, and T[K] reads the property type at that key. Mapped types have their own page with modifiers and key remapping.

Frequently Asked Questions

What does keyof do in TypeScript?

keyof T produces a union of the property names of the type T. For interface User { id: number; name: string }, keyof User is "id" | "name". It works only on types, at compile time, and produces no JavaScript.

What is keyof typeof in TypeScript?

keyof needs a type, but a plain object like const colors = { red: "#f00" } is a value. typeof colors turns the value into its type, and keyof typeof colors then gives the union of its keys, here "red". Writing keyof colors fails with error TS2749.

Why does keyof return string | number?

When the type has a string index signature such as { [key: string]: number }. JavaScript converts numeric property keys to strings, so obj[42] is also a valid access, and TypeScript includes number in the key type. Use Extract<keyof T, string> or string & keyof T if you only want the string keys.

Why does Object.keys return string[] instead of (keyof T)[]?

Because an object can hold more properties than its type lists: a value with an extra field can be assigned to a narrower type. Typing the result as (keyof T)[] would be a lie for those objects. If you know the object has exactly the declared keys, cast: Object.keys(obj) as (keyof typeof obj)[].

How do I fix "Element implicitly has an 'any' type because expression of type 'string' can't be used to index type"?

The index is a plain string, and the object type has no string index signature. Type the parameter as keyof YourType instead of string, or check the string first with a type guard that narrows it to keyof YourType, or give the object an index signature or a Record<string, V> type if it really accepts any key.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED