keyof, typeof & Indexed Access
So far you have written most types by hand. Large programs work better when types are derived: from a value that already exists, or from another type. Then a change in one place updates every type that depends on it.
This lesson covers three small operators that make this possible. typeof gets the type of a value, keyof gets the property names of a type, and indexed access types such as Order["items"] get the type of a property. Together they power most of the advanced types you will meet later.
typeof: the type of a value
In JavaScript, typeof x is a run-time operator that returns a string such as "number". TypeScript adds a second meaning: in a type position (after a colon, in a type alias), typeof x means "the type of the variable x".
const defaultSettings = {
language: "en",
fontSize: 16,
notifications: { sms: true, email: false },
};
type Settings = typeof defaultSettings;
// { language: string; fontSize: number; notifications: { sms: boolean; email: boolean } }
function describe(s: Settings): string {
const channels = Object.entries(s.notifications)
.filter(([, on]) => on)
.map(([name]) => name);
return `${s.language}, ${s.fontSize}px, alerts by ${channels.join(" and ")}`;
}
const priyaSettings: Settings = {
language: "hi",
fontSize: 18,
notifications: { sms: true, email: true },
};
console.log(describe(defaultSettings));
console.log(describe(priyaSettings));
console.log(typeof defaultSettings.fontSize); // the JavaScript typeofen, 16px, alerts by sms hi, 18px, alerts by sms and email number
The last line uses the ordinary JavaScript typeof, which runs and prints a string. The type Settings = typeof defaultSettings line is the TypeScript version, which exists only while compiling. TypeScript tells them apart by where they appear.
Mixing up values and types is a common error:
const config = { retries: 3, timeoutMs: 5000 };
function connect(options: config) {}app.ts(3,27): error TS2749: 'config' refers to a value, but is being used as a type here. Did you mean 'typeof config'?
The message even suggests the fix. You also need typeof for functions, as in ReturnType<typeof fetchSeatMap> from Utility Types.
keyof: the property names of a type
keyof T produces a union of the property names of T, as string literal types.
type Student = { name: string; marks: number; city: string };
type StudentKey = keyof Student; // "name" | "marks" | "city"
function getField<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const meera: Student = { name: "Meera", marks: 93, city: "Kochi" };
const name = getField(meera, "name"); // type: string
const marks = getField(meera, "marks"); // type: number
console.log(name.toUpperCase(), marks + 5);
const columns: StudentKey[] = ["name", "city"];
for (const col of columns) {
console.log(`${col}: ${meera[col]}`);
}MEERA 98 name: Meera city: Kochi
getField is a fully typed version of obj[key]: the key must be a real property name, and the result has the type of that property. That return type, T[K], is an indexed access type, explained below.
A very common combination is keyof typeof, which gets the keys of an object value:
const fares = { SL: 450, "3A": 1200, "2A": 1750 };
type Coach = keyof typeof fares; // "SL" | "3A" | "2A"Indexed access types
An indexed access type looks up the type of a property, using the same square brackets you use to read a property at run time: Order["customer"] is the type of the customer property of Order.
type Order = {
id: number;
customer: { name: string; phone: string };
items: { name: string; price: number; qty: number }[];
status: "placed" | "packed" | "delivered";
};
type Customer = Order["customer"]; // { name: string; phone: string }
type Item = Order["items"][number]; // one element of the items array
type Status = Order["status"]; // "placed" | "packed" | "delivered"
type IdOrStatus = Order["id" | "status"]; // number | "placed" | "packed" | "delivered"
function lineTotal(item: Item): number {
return item.price * item.qty;
}
function label(c: Customer, s: Status): string {
return `${c.name} (${c.phone}) - ${s}`;
}
const order: Order = {
id: 9001,
customer: { name: "Farhan", phone: "99887 76655" },
items: [{ name: "Kurta", price: 899, qty: 2 }, { name: "Dupatta", price: 349, qty: 1 }],
status: "packed",
};
console.log(order.items.map(lineTotal));
console.log(label(order.customer, order.status));[ 1798, 349 ] Farhan (99887 76655) - packed
Two details are worth remembering:
Order["items"][number]means "the type you get when you index the array with any number", which is the element type. This is how you get the item type of an array type.- Indexing with a union, as in
Order["id" | "status"], gives a union of the property types.
You cannot use a variable inside the brackets: Order[key] where key is a const value is an error. Use a type, such as Order[typeof key] or a literal like Order["status"].
Putting them together
Here is the pattern from Literal Types, as const & satisfies, now fully explained. One as const array is the single source of truth, and every type is derived from it:
const MENU = [
{ code: "MD", name: "Masala dosa", price: 90 },
{ code: "ID", name: "Idli vada", price: 70 },
{ code: "FC", name: "Filter coffee", price: 30 },
] as const;
type MenuItem = (typeof MENU)[number];
type MenuCode = MenuItem["code"]; // "MD" | "ID" | "FC"
function priceOf(code: MenuCode): number {
const item = MENU.find((m) => m.code === code);
return item ? item.price : 0;
}
const order: Record<MenuCode, number> = { MD: 2, ID: 1, FC: 3 };
let total = 0;
for (const code of Object.keys(order) as MenuCode[]) {
total += priceOf(code) * order[code];
}
console.log(`Bill: ₹${total}`);Bill: ₹340
Add a fourth item to MENU and MenuCode gains a new member automatically; order then reports a missing key until you add it.
Why Object.keys(order) as MenuCode[]? Because Object.keys() always returns string[]. TypeScript's structural typing means an object may have more properties than its type shows, so the keys could be any strings. When you are sure the object has exactly the declared keys, as here, the assertion is reasonable.
Common mistakes
- Using a value as a type. Write
typeof config, notconfig, in type positions. - Confusing
keyof typeof objandtypeof obj. The first gives the key names; the second gives the whole object type. - Forgetting
[number]for arrays.Order["items"]is the array type;Order["items"][number]is one item. - Expecting
Object.keysto returnkeyof T. It returnsstring[]; assert only when you know there are no extra keys.
Key points
- In a type position,
typeof xgives the type of the valuex; in normal code it is still the JavaScript operator. keyof Tis a union of the property names ofT;keyof typeof objgets the keys of an object value.T["prop"]gets a property's type,T[number]gets an array's element type, and a union index gives a union of types.<T, K extends keyof T>(obj: T, key: K): T[K]is the standard pattern for typed property access.- Deriving types from one
as constsource keeps data and types in sync.
Exercise
A railway app in Jaipur stores fares as const FARES = { SL: 450, "3A": 1200, "2A": 1750, "1A": 2900 } as const;. Derive a type Coach for the keys and a type Fare for the values. Write cheapestAbove(minFare: number): Coach | undefined that returns the cheapest coach whose fare is at least minFare. Log the result for 1000 and for 5000.
Show solution
const FARES = { SL: 450, "3A": 1200, "2A": 1750, "1A": 2900 } as const;
type Coach = keyof typeof FARES; // "SL" | "3A" | "2A" | "1A"
type Fare = (typeof FARES)[Coach]; // 450 | 1200 | 1750 | 2900
function cheapestAbove(minFare: number): Coach | undefined {
const coaches = Object.keys(FARES) as Coach[];
const eligible = coaches.filter((c) => FARES[c] >= minFare);
return eligible.toSorted((a, b) => FARES[a] - FARES[b])[0];
}
const sleeper: Fare = FARES.SL;
console.log(sleeper); // 450
console.log(cheapestAbove(1000)); // 3A
console.log(cheapestAbove(5000)); // undefinedkeyof typeof FARES gives the coach names and indexing the object type with them gives the fare values, so both types come straight from the data.
Quick Quiz
1. What does type T = typeof settings do when written in a type alias?
In a type position, typeof reads the type of a value.
2. For type P = { id: number; name: string }, what is keyof P?
keyof produces a union of the property names.
3. How do you get the element type of type Cart = { items: Product[] }?
Indexing an array type with number gives its element type.
4. What does Object.keys(obj) return in TypeScript?
An object may have extra properties at run time, so the keys are typed as plain strings.
Learn TypeScript with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
