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.
typeofs — same word, two worldsJust like the :/=> lesson, typeof means different things by
world:
| world | what typeof x does | result |
|---|---|---|
| value (JS runtime) | asks the runtime tag of a value | a string like "string", "object" |
type (after : / in a type) | lifts a value into the type world | the 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 keysHere 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 moveChain 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.
T[K] — look up the type at a keyA 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.
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.
Recall, don’t re-read. (Answers reveal on click.)
type T = typeof user does what?T = { id: number; name: string }, then keyof T is…callback: (event: EventMap[TType]) => void, what is EventMap[TType]?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.
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.