Mapped Types & Template Literal Types
📖 Concept
Mapped types iterate over keys of a type and transform each property — they're the for...in loop of the type system. All utility types like Partial, Readonly, and Pick are built with mapped types.
Syntax: { [K in keyof T]: NewType }
Key modifiers:
?/-?— Add or remove optionalreadonly/-readonly— Add or remove readonlyas NewKey— Remap keys (TS 4.1+)
Template literal types bring string manipulation to the type level: \hello-${string}``. Combined with unions, they generate all string combinations at compile time.
Key template literal utilities: Uppercase, Lowercase, Capitalize, Uncapitalize
Mapped types + template literals together enable extremely powerful patterns:
- Generating getter/setter method names from property names
- Creating event name types from state keys
- Building CSS class name types
- Type-safe i18n keys
🏠 Real-world analogy: Mapped types are like a stamping machine on an assembly line. Each item (property) that passes through gets the same modification (transformation). Template literal types are like a label printer — you feed in variables and get formatted labels.
💻 Code Example
1// Basic mapped type (how Partial works internally)2type MyPartial<T> = {3 [K in keyof T]?: T[K];4};56// Remove optional (Required internally)7type MyRequired<T> = {8 [K in keyof T]-?: T[K];9};1011// Make all properties readonly12type MyReadonly<T> = {13 readonly [K in keyof T]: T[K];14};1516// Remove readonly17type Mutable<T> = {18 -readonly [K in keyof T]: T[K];19};2021// Transform value types22type Stringify<T> = {23 [K in keyof T]: string;24};25type Nullify<T> = {26 [K in keyof T]: T[K] | null;27};2829// ⭐ Key remapping with 'as' (TS 4.1+)30interface User {31 name: string;32 age: number;33 email: string;34}3536// Generate getter names37type Getters<T> = {38 [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];39};40type UserGetters = Getters<User>;41// { getName: () => string; getAge: () => number; getEmail: () => string }4243// Generate event handler names44type OnChangeHandlers<T> = {45 [K in keyof T as `on${Capitalize<string & K>}Change`]: (value: T[K]) => void;46};47type UserHandlers = OnChangeHandlers<User>;48// { onNameChange: (value: string) => void; onAgeChange: ... }4950// Filter keys by value type51type StringKeysOnly<T> = {52 [K in keyof T as T[K] extends string ? K : never]: T[K];53};54type StringProps = StringKeysOnly<User>;55// { name: string; email: string } (age removed!)5657// Template literal types58type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";59type ApiRoute = `/api/${string}`;60type TypedRoute = `/api/${"users" | "posts" | "comments"}`;61// "/api/users" | "/api/posts" | "/api/comments"6263// Cartesian product with template literals64type Color = "red" | "blue" | "green";65type Size = "sm" | "md" | "lg";66type ClassName = `${Color}-${Size}`;67// "red-sm" | "red-md" | "red-lg" | "blue-sm" | ... (9 combinations)6869// Built-in string transformers70type Greeting = "hello world";71type Upper = Uppercase<Greeting>; // "HELLO WORLD"72type Lower = Lowercase<"HELLO">; // "hello"73type Cap = Capitalize<"hello">; // "Hello"74type Uncap = Uncapitalize<"Hello">; // "hello"7576// Practical: derive event names from object keys77type EventNames<T> = {78 [K in keyof T]: `on${Capitalize<string & K>}`;79}[keyof T];80type UserEvents = EventNames<User>;81// "onName" | "onAge" | "onEmail"
🏋️ Practice Exercise
Mini Exercise:
- Implement
MyPick<T, K>using a mapped type - Create
Nullable<T>that makes all propertiesT[K] | null - Use key remapping to create
Setters<T>—setName(value: string): void - Build a template literal type for CSS utility classes like
"text-sm","bg-red", etc. - Create
FilterByType<T, U>that keeps only properties of type U
⚠️ Common Mistakes
Forgetting
string & Kwhen usingCapitalizein key remapping —keyof Tcan includesymbol, which can't be capitalizedNot understanding that mapped types create NEW types — they don't modify the original; types are always immutable
Using
as neverin key remapping to filter — this is correct behavior (never keys are removed), but it's confusing at firstTemplate literal type explosion — combining large unions creates a cartesian product that can slow the compiler
Confusing mapped types with index signatures —
{ [K in keyof T]: T[K] }iterates known keys;{ [key: string]: T }accepts any string key
💼 Interview Questions
🎤 Mock Interview
Practice a live interview for Mapped Types & Template Literal Types