Back to Curriculum

Mapped Types and Template Literal Types

📚 Lesson 14 of 15 ⏱️ 40 min

Mapped Types and Template Literal Types

40 min

Mapped types are a powerful TypeScript feature that enables you to create new types by transforming properties of existing types. They use the `in` keyword to iterate over keys of a type and apply transformations, similar to how `Array.map()` transforms array elements. Mapped types are the foundation of utility types like `Readonly<T>`, `Partial<T>`, and `Pick<T, K>`, and enable you to create your own type transformations.

The basic syntax of a mapped type is `{ [K in keyof T]: Transformation }`, where K represents each key in type T, and Transformation is applied to each property. You can add modifiers like `readonly` or `?` to make properties readonly or optional. You can also remove modifiers using `-readonly` or `-?`. This flexibility enables you to create variations of types while maintaining type relationships.

Template literal types, introduced in TypeScript 4.1, enable you to create string literal types by combining other string literal types. They use template literal syntax (backticks and `${}`) to concatenate string types. For example, ``Hello, ${Name}`` creates a type that's a template of "Hello, " followed by the Name type. This is powerful for creating type-safe string patterns, API endpoints, CSS class names, and more.

Template literal types work with string manipulation utilities like `Uppercase<T>`, `Lowercase<T>`, `Capitalize<T>`, and `Uncapitalize<T>`. These utilities transform string literal types, enabling you to create consistent naming conventions, convert between naming styles (camelCase, PascalCase, snake_case), and generate related type names automatically.

Combining mapped types with template literal types enables sophisticated type transformations. For example, you can create a type that generates getter method names from property names, or create API endpoint types from resource names. This pattern is used in libraries to generate type-safe APIs, create type-safe routing systems, and build configuration types that adapt to your data structures.

Practical applications include creating type-safe event handler maps (where event names are keys and handler types are values), generating API client types from OpenAPI schemas, creating type-safe CSS-in-JS prop types, and building type-safe configuration objects. These features enable you to catch string-related errors at compile time and provide excellent IntelliSense support for dynamic string patterns.

Key Concepts

  • Mapped types transform properties of existing types using the in keyword.
  • Template literal types create string types by combining other string types.
  • String manipulation utilities (Uppercase, Lowercase, Capitalize) transform string types.
  • Mapped types and template literals can be combined for powerful transformations.
  • These features enable type-safe string patterns and dynamic type generation.

Learning Objectives

Master

  • Creating mapped types to transform object properties
  • Using template literal types for type-safe string patterns
  • Combining mapped types with template literals
  • Applying string manipulation utilities to transform types

Develop

  • Advanced type transformation thinking
  • Understanding type generation and code generation patterns
  • Designing type-safe APIs and configurations

Tips

  • Use mapped types when you need to transform all properties of a type.
  • Use template literal types for type-safe string concatenation and patterns.
  • Combine mapped types with template literals for sophisticated transformations.
  • Study built-in utility types to learn mapped type patterns.

Common Pitfalls

  • Creating overly complex mapped types that are hard to understand.
  • Not understanding how template literal types distribute over unions.
  • Using mapped types when simple type aliases would suffice.
  • Forgetting that these transformations are compile-time only.

Summary

  • Mapped types transform properties of types using iteration syntax.
  • Template literal types create string types from other string types.
  • String manipulation utilities enable consistent naming transformations.
  • Combining these features enables sophisticated type-safe patterns.
  • They're essential for type-safe APIs, configurations, and code generation.

Exercise

Use mapped types and template literal types to create type-safe configurations.

type EventName = 'click' | 'hover' | 'focus';
type EventHandler<T> = (event: T) => void;

type EventHandlers = {
  [K in EventName]: EventHandler<Event>;
};

type ApiEndpoint = 'users' | 'posts' | 'comments';
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

type ApiUrl = `/api/${ApiEndpoint}`;
type ApiConfig = Record<ApiUrl, HttpMethod>;

const handlers: EventHandlers = {
  click: (e) => console.log('clicked'),
  hover: (e) => console.log('hovered'),
  focus: (e) => console.log('focused')
};

const apiConfig: ApiConfig = {
  '/api/users': 'GET',
  '/api/posts': 'POST',
  '/api/comments': 'PUT'
};

Exercise Tips

  • Use keyof to create mapped types: type Readonly<T> = { readonly [P in keyof T]: T[P] };
  • Add modifiers: type Optional<T> = { [P in keyof T]?: T[P] };
  • Use template literal types for string manipulation: type Getter<T> = `get${Capitalize<T>}`;
  • Combine mapped types with conditional types for complex transformations.

Code Editor

Output