Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Combining Types  ›  Capstone Project

The keyof Operator

keyof Operator 12 minkeyof T produces the union of T's property names as string literal types, so you can accept only keys that really exist.

The keyof Operator

The tracker lets users sort tasks by any column: id, title or done. A sortBy(key: string) function accepts "titel" just as happily as "title", and the bug only appears when the list refuses to sort. keyof lets the compiler know exactly which keys exist.

The rule

keyof T is the union of T's property names.

type Task = { id: number; title: string; done: boolean };

type TaskKey = keyof Task;   // "id" | "title" | "done"

let k: TaskKey = "title";    // fine
k = "titel";                 // error: not a key of Task

keyof is a type operator: it takes an object type and produces a union of string literal types, one per property. Add a property to Task and TaskKey updates itself.

Its natural partner is the indexed access type T[K], which looks up the type of a property:

type TitleType = Task["title"];        // string
type AnyValue = Task[keyof Task];      // number | string | boolean

Put them together with a generic function and you get a property reader that is safe for every key:

function getField<K extends keyof Task>(task: Task, key: K): Task[K] {
  return task[key];
}

const t: Task = { id: 1, title: "Buy milk", done: false };
getField(t, "done");    // type: boolean
getField(t, "title");   // type: string
getField(t, "owner");   // error: "owner" is not a key of Task

K extends keyof Task means "K must be one of Task's keys". The return type Task[K] then follows whichever key was passed. Do not worry if generics are new — here the pattern is all you need.

keyof also pairs with typeof, which turns a value into its type:

const labels = { todo: "To do", done: "Done" };
type LabelKey = keyof typeof labels;   // "todo" | "done"

The shortcut

To work out keyof X, list the property names of X and put quotes around each. That list, joined as a union, is the answer.

A safe sortByStep 1 of 4
  1. List the keys

    Task has id, title, done, so keyof Task is "id" | "title" | "done".

Drills

Answer fast — trust the rule.

quick drill

What is keyof { x: number; y: number }?

quick drill

With type Task = { id: number; title: string; done: boolean }, what is Task["done"]?

quick drill

function getField<K extends keyof Task>(task: Task, key: K): Task[K] — which call is an error?

quick drill

What does keyof typeof labels give for const labels = { todo: "To do", done: "Done" }?

Remember

  • keyof T is the union of T's property names.
  • List the property names, add quotes, join with a union — that is keyof.
  • Trap: key: string accepts typos; key: keyof T does not.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainA safe property getter

Explain what keyof Task is for type Task = { id: number; title: string; done: boolean }, and how function get<K extends keyof Task>(task: Task, key: K): Task[K] prevents a bug that (task: Task, key: string) would allow.