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.
TypeScript provides two key operators to bridge the gap between runtime JavaScript values and compile-time types: typeof and keyof.
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;
};
*/
keyof OperatorThe 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
keyof typeofExtract 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"
typeof x === "string" evaluates a string at runtime. Type-space type T = typeof x queries static compile-time type shapes!Use keyof typeof for type-safe dictionary lookups. Next, let's explore Conditional Types and the infer keyword!
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.
`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.
`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.