Back to TypeScript tutorials
Advanced15 min read

Advanced Type Features

Mapped types, template literals, utility types, and inference for sophisticated type-level programming.

Mapped Types

Mapped types transform properties of an existing type: `type ReadonlyUser = { readonly [K in keyof User]: User[K] }`. The `in keyof T` loop iterates property names at the type level.

Built-in utilities `Partial`, `Required`, `Readonly`, and `Record` are implemented as mapped types. Use them before writing custom versions — they are battle-tested and familiar to contributors.

  • `[K in keyof T]` transforms properties
  • Built-in utilities use mapped types
  • Prefer utilities over custom reimplementation
type PartialUser = Partial<User>;
type UserFlags = Record<keyof User, boolean>;

Template Literal Types

Template literal types build string types from fragments: `type EventName = `on${Capitalize<Kind>}``. They power typed CSS variables, route paths, and event name generation in design systems.

Combine with union distribution: when a union is used in a template, TypeScript distributes over each member. This enables exhaustive string pattern generation from const unions.

  • String types from template patterns
  • Distribution over unions
  • Typed routes and event names
type Route = `/users/${string}` | '/users';
type Getter = `get${Capitalize<string>}`;

Utility Types in Practice

`Pick<T, K>` and `Omit<T, K>` select or exclude properties. `Extract<T, U>` and `Exclude<T, U>` filter union members. `NonNullable<T>` removes null and undefined. `ReturnType<T>` and `Parameters<T>` extract function signature parts.

`Awaited<T>` unwraps Promise types — essential for typing async function returns. Compose utilities rather than duplicating large type definitions manually.

  • Pick/Omit for property selection
  • ReturnType/Parameters for functions
  • Awaited for Promise unwrapping
type UserPreview = Pick<User, 'id' | 'name'>;
type CreateUserParams = Parameters<typeof createUser>[0];

Type Inference with typeof and infer

`typeof value` in type position extracts the type of a value — useful for deriving types from const config objects and zod schemas. `infer` inside conditional types extracts nested types, powering library magic like React component prop inference.

`satisfies` operator (TS 4.9+) validates that a value matches a type while preserving the narrower inferred literal type — ideal for config objects and design tokens.

  • `typeof` for value-to-type extraction
  • `satisfies` preserves literal inference
  • `infer` for nested type extraction
const routes = {
  home: '/',
  profile: '/profile',
} as const satisfies Record<string, string>;

When to Stop Abstracting

Advanced types improve library DX but can obscure application code. If a type requires a paragraph to explain, consider simplifying the runtime API instead.

Reach for advanced types when the same pattern repeats across dozens of callsites — form field paths, API route typing, or action dispatch maps. Otherwise, explicit interfaces remain clearer for most team members.

  • Complex types obscure application code
  • Use for repeated cross-cutting patterns
  • Prefer simple interfaces when sufficient

Get In Touch


Ready to discuss your next project? Drop me a message.