The keyof & typeof Type Operators
Quick Overview
`typeof` lifts a runtime value into the type world, and `keyof` turns a type into the union of its keys. Chained as `keyof typeof config` they derive the valid key list from the object itself.
Deep Dive
1. Querying Runtime Code in Type Space
TypeScript provides two key operators to bridge the gap between runtime JavaScript values and compile-time types: typeof and keyof.
2. The typeof Operator (in Type Space)
In type space, typeof queries the static TypeScript type of a runtime JavaScript variable or object:
const appConfig = {
apiEndpoint: "https://api.devloom.dev",
timeout: 5000,
maxRetries: 3,
};
// Extract type shape directly from the runtime variable!
type AppConfig = typeof appConfig;
/*
Inferred as:
type AppConfig = {
apiEndpoint: string;
timeout: number;
maxRetries: number;
};
*/
3. The keyof Operator
The keyof operator takes an object type and produces a string or numeric union of its property keys:
type User = {
id: number;
name: string;
email: string;
};
type UserKeys = keyof User; // Inferred as: "id" | "name" | "email"
const key: UserKeys = "name"; // Valid!
// const badKey: UserKeys = "age"; // Error: Type '"age"' is not assignable
4. Combining keyof typeof
Extract property keys directly from a runtime JavaScript object:
const COLORS = {
primary: "#3178C6",
secondary: "#F7DF1E",
accent: "#FF4081",
} as const;
type ColorKey = keyof typeof COLORS; // "primary" | "secondary" | "accent"
5. Common Mistakes
- Confusing Runtime typeof with Type-Space typeof: Runtime
typeof x === "string"evaluates a string at runtime. Type-spacetype T = typeof xqueries static compile-time type shapes!
6. Next Steps
Use keyof typeof for type-safe dictionary lookups. Next, let's explore Conditional Types and the infer keyword!
Next Up in TypeScript & Type System Mechanics
TypeScript Primitives & Explicit Type Annotations
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
Type Inference & Literal Types (const vs. let)
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
Arrays, Tuples & Readonly Modifiers
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.
