keyof & typeof
Advanced · 18 min read · ▶ live playground · ✦ checkpoint
keyof and type-level typeof let you derive types from code you already wrote. keyof turns an object shape into a union of its property names, and type-level typeof is typeof written where TypeScript expects a type, so it copies the checker's type for a real value.
Together they give you the pattern behind keyof typeof config: write the data once, then ask the checker to derive the allowed keys from it.
keyof reads the labels
The keyof operator is a type-level tool that turns an object type into a union of its property keys. A key union is a union of literal property names, such as "name" | "plan" | "karma".
Think of keyof as reading the labels off an object shape. It does not read the values inside the drawers; it reads the labels on the drawers.
ProfileField is the set "name" | "plan" | "karma". If you add avatarUrl to UserProfile, ProfileField grows. If you remove karma, the list of visible fields stops compiling until you update it.
That is the maintenance win. The type does not repeat the keys. It asks the original shape for them.
typeof has two jobs
You already met JavaScript's runtime typeof: typeof value runs in JavaScript and returns a string like "string" or "object". TypeScript also has a type-level version.
A type position is a place where TypeScript expects a type, such as after type Launch = or after a colon in an annotation. In a type position, typeof sampleLaunch means "the type of the value named sampleLaunch."
The first typeof sampleLaunch is ordinary JavaScript, so it prints "object". The second one lives in type Launch = typeof sampleLaunch, so it produces the object type { code: string; year: number; crew: number }.
Type-level typeof is a snapshot of what the checker currently knows about a value. In this example, code is string, not the literal type "apollo-11", because object properties normally widen unless you ask for more precision with as const.
keyof typeof config
Now combine the two operators. typeof moves from a value to its type. keyof moves from that type to its key union.
const config = {
dashboard: true,
karmaBoost: false,
};
type ConfigKey = keyof typeof config;Read that right to left:
typeof config: get the type of the value namedconfig.keyof ...: get the keys of that type.ConfigKey: the set of valid config keys.
Here is the full pattern with a feature config. A source of truth is the one place you edit a fact, while other facts are derived from it.
FeatureName is "dashboard" | "karmaBoost" | "launchNotes". You did not write that union by hand. You wrote the runtime config object, then asked the checker to derive the key set from it.
The satisfies FeatureTable part comes from lesson 8.3: it checks that each feature entry has the right shape. The as const part comes from lesson 8.2: it keeps useful literal precision. The new move is keyof typeof featureConfig, which turns the real config keys into a type.
The key union would still work without as const; this config uses it because exact config values are useful too.
Stop writing the same keys twice
The old pattern looks harmless:
const featureConfig = {
dashboard: { label: "Dashboard" },
karmaBoost: { label: "Karma boost" },
};
type FeatureName = "dashboard" | "karmaBoost";But the keys now live in two places. Add launchNotes to the object and forget the union, and callers cannot use the new feature. Remove karmaBoost from the object and forget the union, and the type still claims a dead key is valid.
With keyof typeof, the type follows the data:
const featureConfig = {
dashboard: { label: "Dashboard" },
karmaBoost: { label: "Karma boost" },
};
type FeatureName = keyof typeof featureConfig;This is type manipulation at its best: not cleverness for its own sake, but deleting a second promise you would otherwise have to maintain by hand.
Next, you will use those key unions to reach inside shapes. keyof gives you the key set; indexed access types show the value type for a selected key.
Checkpoint
Answer all three to mark this lesson complete