Mapped Types, keyof, and Template Literal Types (Interview-Ready)
How keyof + mapped types work, and how template literal types unlock powerful typed APIs.
Frontend Interview Team
February 08, 2026
What you’ll learn
- What
keyofand mapped types really produce - How to build safer APIs with
Record,Partial,Pick,Omit, and custom mapped types - How template literal types unlock ergonomic, strongly-typed “string APIs”
- Interview-ready patterns + common mistakes
30‑second interview answer
keyof T gives you the union of property keys of T. A mapped type like { [K in keyof T]: ... } lets you transform each property (e.g., make it optional, readonly, or change the value type). Template literal types let you create or constrain string unions (like 'onClick' | 'onChange') from other unions — useful for typed event systems and config keys.
Mental model
TypeScript has type-level loops.
keyof T→ “the keys of T” (a union)K in ...→ “iterate over each key in this union”[K]→ the current key in the loop
So mapped types are basically:
“For each key K in the keys of T, produce a property K with some new type.”
keyof basics
type User = {
id: string;
name: string;
isAdmin?: boolean;
};
type UserKeys = keyof User;
// "id" | "name" | "isAdmin"Two common interview follow-ups:
keyofincludes optional keys too (likeisAdmin).- If
Thas an index signature,keyofcan widen tostring | number.
Mapped types: the core pattern
type Readonlyify<T> = {
readonly [K in keyof T]: T[K];
};
type Optionalify<T> = {
[K in keyof T]?: T[K];
};Mapped type modifiers
You can add/remove modifiers:
type Requiredify<T> = { [K in keyof T]-?: T[K] };
type Mutable<T> = { -readonly [K in keyof T]: T[K] };Pick/Omit are just mapped types
type Pick2<T, K extends keyof T> = {
[P in K]: T[P];
};
type Omit2<T, K extends keyof T> = Pick2<T, Exclude<keyof T, K>>;Interview point:
Omit<T, K>is basicallyPick<T, keys-you-didn’t-omit>.
Key remapping (as) — extremely useful
You can rename keys inside the mapped type:
type Prefixed<T> = {
[K in keyof T as `app_${Extract<K, string>}`]: T[K];
};
type U = Prefixed<{ id: string; name: string }>;
// { app_id: string; app_name: string }Notes:
Extract<K, string>is needed because template literal types expectstring-like keys.
Template literal types
Building unions
type EventName = 'click' | 'change';
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onChange"Constraining strings
type Route = `/${string}`;
function go(to: Route) {}
go('/home'); // ✅
go('home'); // ❌Practical pattern: typed event emitter API
type Events = {
click: { x: number; y: number };
change: { value: string };
};
type OnHandlers<E> = {
[K in keyof E as `on${Capitalize<Extract<K, string>>}`]: (payload: E[K]) => void;
};
type Handlers = OnHandlers<Events>;
/*
{
onClick: (payload: {x:number;y:number}) => void;
onChange: (payload: {value:string}) => void;
}
*/This is the kind of thing seniors do to make APIs hard to misuse.
Common mistakes (interview + real world)
- Forgetting
Extract<K, string>when using template literals with keys - Overusing mapped types where a normal type is clearer
- Creating “clever” types that make error messages unreadable
Rule of thumb:
- If it improves API safety and DX, great.
- If it makes the type errors impossible to debug, simplify.
Interview questions to practice
- What’s the difference between
keyof TandT[keyof T]? - How does
Omitwork internally? - What is key remapping and when would you use it?
- How do template literal types help with safer string APIs?
Quick recap
keyofgives the key union.- Mapped types loop over keys to transform shape.
- Key remapping + template literals are powerful for ergonomic, safe APIs.