Mapped Types, keyof, and Template Literal Types (Interview-Ready)

How keyof + mapped types work, and how template literal types unlock powerful typed APIs.

F

Frontend Interview Team

February 08, 2026

2 min read
Mapped Types, keyof, and Template Literal Types (Interview-Ready)

What you’ll learn

  • What keyof and 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:

  1. keyof includes optional keys too (like isAdmin).
  2. If T has an index signature, keyof can widen to string | 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 basically Pick<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 expect string-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

  1. What’s the difference between keyof T and T[keyof T]?
  2. How does Omit work internally?
  3. What is key remapping and when would you use it?
  4. How do template literal types help with safer string APIs?

Quick recap

  • keyof gives the key union.
  • Mapped types loop over keys to transform shape.
  • Key remapping + template literals are powerful for ergonomic, safe APIs.