The keyof operator lets code choose a property name while keeping the result type connected.

keyof `keyof` creates a union of the property names from an object type.

Keyof and Indexed Access

field
keyof.ts
Replay: real traced execution (multi-file project)
type Course = {
    title: string;
    minutes: number;
    complete: boolean;
};

function getValue<T, K extends keyof T>(object: T, key: K): T[K] {
    return object[key];
}

const field: keyof Course = "minutes";
const course: Course = { title: "Generics", minutes: 25, complete: false };
const value: Course[typeof field] = getValue<Course, typeof field>(course, field);

console.log(`${field}=${value}`);
type Course = {
    title: string;
    minutes: number;
    complete: boolean;
};

function getValue<T, K extends keyof T>(object: T, key: K): T[K] {
    return object[key];
}

const field: keyof Course = "title";
const course: Course = { title: "Generics", minutes: 25, complete: false };
const value: Course[typeof field] = getValue<Course, typeof field>(course, field);

console.log(`${field}=${value}`);
type Course = {
    title: string;
    minutes: number;
    complete: boolean;
};

function getValue<T, K extends keyof T>(object: T, key: K): T[K] {
    return object[key];
}

const field: keyof Course = "complete";
const course: Course = { title: "Generics", minutes: 25, complete: false };
const value: Course[typeof field] = getValue<Course, typeof field>(course, field);

console.log(`${field}=${value}`);
  1. field ← minutes, course ← [object Object]

    8    return object[key];9}1011const field→ minutes: keyof Course = "minutes";  //@field="title", "complete"12const course→ [object Object]: Course = { title: "Generics", minutes: 25, complete: false };13const value: Course[typeof field] = getValue<Course, typeof field>(course[object Object], fieldminutes);
  2. function getValue<T, K extends keyof T>(object: T, key: K): T[K]

    4    complete: boolean;5};67function getValue<T, K extends keyof T>(object[object Object]: T, keyminutes: K): T[K] {8    return object[key]25;9}
  3. value ← 25

    11const field: keyof Course = "minutes";  //@field="title", "complete"12const course: Course = { title: "Generics", minutes: 25, complete: false };13const value→ 25: Course[typeof field] = getValue<Course, typeof field>(course[object Object], fieldminutes);1415console.log(`${fieldminutes}=${value25}`);
    outputminutes=25
  1. field ← title, course ← [object Object]

    8    return object[key];9}1011const field→ title: keyof Course = "title";12const course→ [object Object]: Course = { title: "Generics", minutes: 25, complete: false };13const value: Course[typeof field] = getValue<Course, typeof field>(course[object Object], fieldtitle);
  2. function getValue<T, K extends keyof T>(object: T, key: K): T[K]

    4    complete: boolean;5};67function getValue<T, K extends keyof T>(object[object Object]: T, keytitle: K): T[K] {8    return object[key]Generics;9}
  3. value ← Generics

    11const field: keyof Course = "title";12const course: Course = { title: "Generics", minutes: 25, complete: false };13const value→ Generics: Course[typeof field] = getValue<Course, typeof field>(course[object Object], fieldtitle);1415console.log(`${fieldtitle}=${valueGenerics}`);
    outputtitle=Generics
  1. field ← complete, course ← [object Object]

    8    return object[key];9}1011const field→ complete: keyof Course = "complete";12const course→ [object Object]: Course = { title: "Generics", minutes: 25, complete: false };13const value: Course[typeof field] = getValue<Course, typeof field>(course[object Object], fieldcomplete);
  2. function getValue<T, K extends keyof T>(object: T, key: K): T[K]

    4    complete: boolean;5};67function getValue<T, K extends keyof T>(object[object Object]: T, keycomplete: K): T[K] {8    return object[key]false;9}
  3. value ← false

    11const field: keyof Course = "complete";12const course: Course = { title: "Generics", minutes: 25, complete: false };13const value→ false: Course[typeof field] = getValue<Course, typeof field>(course[object Object], fieldcomplete);1415console.log(`${fieldcomplete}=${valuefalse}`);
    outputcomplete=false

Follow the Key

  1. field starts as minutes.
  2. course holds title="Generics", minutes=25, and complete=false.
  3. getValue(course, field) reads the property named by field.
  4. With field="minutes", the value is 25.
  5. The program prints minutes=25. | field | course value read | printed output | | --- | --- | --- | | minutes | 25 | minutes=25 | | title | Generics | title=Generics | | complete | false | complete=false |

Exercise: keyof.ts

Reproduce minutes=25, then use field title and complete to predict title=Generics and complete=false.