FrontendPrep
Menu
Topics
Questions
Guides
Challenges
Soon
Back to TypeScript Questions
typescriptHard

TypeScript: Mapped Types and Template Literal Types

Understand advanced TypeScript Mapped Types and Template Literal Types. Learn how to map, filter, and remap object keys dynamically using conditional and utility expressions.

TypeScript: Mapped Types and Template Literal Types

An advanced TypeScript type-safety question is:

What are Mapped Types and Template Literal Types in TypeScript? How do you use key remapping (the as clause) to dynamically transform object key names or filter keys during compilation?

Mapped types let you take an existing type and transform each of its properties into a new type. Combined with template literal types (introduced in TS 4.1), you can build complex, automated type-safety transformations for state stores, API clients, and theme configurations.


1. What is a Mapped Type?

A mapped type is a generic type that iterates over a union of keys (usually created via keyof) to construct an object type:

type ReadOnly<T> = {
  readonly [P in keyof T]: T[P];
};
 
interface User {
  id: number;
  name: string;
}
 
type ReadOnlyUser = ReadOnly<User>;
// Result:
// {
//   readonly id: number;
//   readonly name: string;
// }

Here, [P in keyof T] acts as a compile-time loop that maps over each property name P in keyof T and resolves its value type as T[P].


2. Using Mapping Modifiers

You can add or remove property modifiers (like readonly or optional ?) during the mapping loop using prefix operators + (default) or -:

// Remove the optional modifier from all properties
type Concrete<T> = {
  [P in keyof T]-?: T[P];
};
 
interface MaybeUser {
  id: number;
  name?: string;
  age?: number;
}
 
type StrictUser = Concrete<MaybeUser>;
// Result: { id: number; name: string; age: number; }

3. Template Literal Types

Template literal types have the same syntax as ES6 template strings but are resolved entirely at the type level. They combine strings to generate new unions:

type Position = "Top" | "Bottom";
type Alignment = "Left" | "Right";
 
type BoxAnchor = `${Position}-${Alignment}`;
// Result: "Top-Left" | "Top-Right" | "Bottom-Left" | "Bottom-Right"

TypeScript also provides utility types for string transformations: Uppercase, Lowercase, Capitalize, and Uncapitalize.


4. Key Remapping with as

Key remapping lets you change key names or exclude specific keys altogether during the mapping loop using the as keyword.

A. Renaming Keys (Getter Generation)

Suppose you want to take an object and dynamically generate a type representing all its getter methods:

type Getters<T> = {
  [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
 
interface Person {
  name: string;
  age: number;
}
 
type PersonGetters = Getters<Person>;
// Result:
// {
//   getName: () => string;
//   getAge: () => number;
// }

B. Filtering Keys (Key Omission)

You can filter out keys by remapping them to never. If a key evaluates to never, TypeScript excludes it from the final object:

// Filter out properties that are not functions
type FunctionPropertiesOnly<T> = {
  [P in keyof T as T[P] extends Function ? P : never]: T[P];
};
 
interface Controller {
  data: string[];
  fetchData: () => Promise<void>;
  saveData: () => void;
}
 
type ControllerActions = FunctionPropertiesOnly<Controller>;
// Result:
// {
//   fetchData: () => Promise<void>;
//   saveData: () => void;
// }

Senior-Level Interview Answer

Mapped types iterate over a set of keys to dynamically generate object structures. Template literal types build upon string union types, allowing string concatenations and modifications to resolve at compile-time. We can combine these features using key remapping via the as clause to rename keys dynamically (e.g. capitalizing properties to output getter signatures) or filter out keys. By resolving key definitions to never inside a conditional type check during the as remapping cycle, TypeScript excludes those properties entirely, providing a declarative mechanism to implement custom utility transformations.


Common Interview Mistakes

❌ Forgetting the string & P constraint in template strings

When mapping properties (P in keyof T), keyof T can include strings, numbers, or symbol types. If you try to concatenate P inside a template literal directly (like ${P}), TypeScript will throw a compilation error. You must constraint P by intersection, e.g., ${string & P}, or check its type using conditionals.

❌ Using mapped types inside interfaces

Mapped types are type aliases and cannot be declared inside standard interfaces. Attempting to write interface MyObj { [K in keyof T]: T[K] } is syntactically invalid. You must use type MyObj<T> = { [K in keyof T]: T[K] }.


Key Takeaways

  • Type Loop: Mapped types behave like compile-time loops, mapping over unions of keys to generate object definitions.
  • Modifiers Tuning: Prefix symbols + and - add or remove modifiers (like optional ? or readonly) during mapping loops.
  • Literal Union: Template literal types evaluate string patterns at the type level to compile complex combinations.
  • Key Remapping: The as keyword renames keys dynamically or filters them based on conditional type evaluations.
  • Exclusion via Never: Evaluating a key as never inside a key remapping clause removes that property from the resulting type.

Share this Resource

Help other developers level up by sharing this study guide.

More Technical Questions

Expand your mastery. Deep dive into other frontend interview challenges in this category.