Lesson 8 · Module 1.3 — Core type vocabulary

keyof, the two typeofs, and indexed access T[K]

It is common to use typeof two ways and keyof typeof as one unit, then index into a type with EventMap[TType]. These are the operators that let a type be computed from a real object instead of hand-written. Read them together and a whole class of “derived” types opens up.

The two typeofs — same word, two worlds

Just like the :/=> lesson, typeof means different things by world:

worldwhat typeof x doesresult
value (JS runtime)asks the runtime tag of a valuea string like "string", "object"
type (after : / in a type)lifts a value into the type worldthe value’s static type
if (typeof x === "string") { ... }   // VALUE world: runtime check, returns "string"

const flags = { ONE: () => 1, TWO: () => 2 };
type Flags = typeof flags;            // TYPE world: { ONE: () => number; TWO: () => number }

The first is plain JavaScript. The second runs at compile time only: it reads the value flags and hands you its type. That second one is the bridge from your real objects into the type system.

keyof — the union of an object type’s keys

Here is the classic constrained reader:

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

Read keyof T as “the union of all property names of T.” If T is { id: number; name: string }, then keyof T is "id" | "name". The constraint K extends keyof T means “key must be one of the real keys” — that is why obj[key] is safe.

keyof typeof — keys of a value, in one move

Chain them: take a value, lift it to a type with typeof, then take its keys with keyof:

const Flags = { ONE: () => "one", TWO: () => "two" };
type FlagsType = typeof Flags;     // value → type
type FlagKeys = keyof FlagsType;     // type → "ONE" | "TWO"
// or in one breath:  type FlagKeys = keyof typeof Flags;

Read keyof typeof X right-to-left as a pipeline: take the value X → get its type → get the union of its keys. It is how a value defined once becomes the single source of truth for its own key union.

Indexed access T[K] — look up the type at a key

A classic event-map example puts it all together. EventMap[TType] reads the value type stored at key TType:

type EventMap = {
  "click": { x: number, y: number };
  "keydown": { key: string };
};

function addHandler<TType extends keyof EventMap>(
  type: TType,
  callback: (event: EventMap[TType]) => void   // ← indexed access
) {}

addHandler("click", (event) => { event.x; });   // event is { x: number, y: number }
addHandler("keydown", (event) => { event.key; }); // event is { key: string }

Read EventMap["click"] the same way you read obj["click"] in JavaScript — but it returns a type. Because TType extends keyof EventMap stays the exact literal "click", EventMap[TType] resolves to that one event’s shape. Same bracket syntax as runtime indexing, lifted into the type world.

Read it this way

keyof → “union of key names.” typeof value (in a type) → “the type of this value.” T[K] → “the type sitting at key K.” Chain them and you can derive a whole API of types from one object literal.

Check yourself

Recall, don’t re-read. (Answers reveal on click.)

In a type annotation, type T = typeof user does what?
If T = { id: number; name: string }, then keyof T is…
In callback: (event: EventMap[TType]) => void, what is EventMap[TType]?

Primary source

The handbook pages are keyof Type Operator, typeof Type Operator, and Indexed Access Types. Best way to feel it: paste the EventMap block into the TS Playground and hover event in each call to watch EventMap[TType] resolve.

Try it yourself

Stuck on a derived type? Read it closely and ask “which of keyof/typeof/ T[K] is doing the work?” Next lesson (1.4): the everyday utility types — Partial, Record, Pick, Omit, Exclude, and recursive DeepPartial.