Template Literal and Mapped Types in TypeScript

A
Author
15 May, 2026 1 min read 1.25K views

Advanced types let you describe APIs precisely: event names, CSS tokens, route parameters. You won't write these every day, but they make library-style code much safer.

Template literal types

type Size = 'sm' | 'md' | 'lg';
type Side = 'top' | 'bottom';
type MarginClass = `m${Capitalize<Side>}-${Size}`;
// 'mTop-sm' | 'mTop-md' | 'mTop-lg' | 'mBottom-sm' | ...

type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickHandler = EventName<'click'>; // 'onClick'

Mapped types

Loop over the keys of a type to build a new one:

type Flags<T> = { [K in keyof T]: boolean };

interface Features { darkMode: string; beta: number }
type FeatureFlags = Flags<Features>; // { darkMode: boolean; beta: boolean }

Key remapping with as

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<{ name: string; age: number }>;
// { getName: () => string; getAge: () => number }

Conditional types and infer

type ElementType<T> = T extends (infer U)[] ? U : T;
type A = ElementType<string[]>; // string

// Pull route params out of a path string
type Params<P extends string> =
  P extends `${string}:${infer Param}/${infer Rest}`
    ? { [K in Param | keyof Params<Rest>]: string }
    : P extends `${string}:${infer Param}`
      ? { [K in Param]: string }
      : {};

type R = Params<'/users/:userId/posts/:postId'>; // { userId: string; postId: string }

Use them wisely

  • Clever types are hard for teammates to read. Prefer simple types in application code.
  • Advanced types shine in shared utilities and libraries, where many callers benefit.
  • Hover over types in your editor to check what they resolve to.
Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.