Skip to content
advanced Phase 2 · TypeScript Advanced Types

Mapped & Conditional Types

Create dynamic types with mapped types, conditional types, and infer.

1h 15m
0 problems
Topic Progress 0%

Mapped Type Syntax

Mapped Type Syntax

Mapped types iterate over keys of an existing type to create a new type.

Basic Mapped Type

// Syntax: { [K in keyof T]: NewType }

type Optional<T> = {
  [K in keyof T]?: T[K];
};

interface User {
  id: number;
  name: string;
  email: string;
}

type OptionalUser = Optional<User>;
// { id?: number; name?: string; email?: string }

// Readonly mapped type
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

type ReadonlyUser = Readonly<User>;
// { readonly id: number; readonly name: string; readonly email: string }

Modifiers in Mapped Types

// Remove readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

// Remove optional
type Required<T> = {
  [K in keyof T]-?: T[K];
};

// Remove both modifiers
type Clean<T> = {
  -readonly [K in keyof T]-?: T[K];
};

Key Remapping

Key Remapping with as

Transforming Property Names

// Prefix all properties
 type Prefixed<T, P extends string> = {
  [K in keyof T as `${P}${Capitalize<string & K>}`]: T[K];
};

interface User {
  name: string;
  age: number;
}

type Getters = Prefixed<User, 'get'>;
// { getName: string; getAge: number }

// Filter properties by value type
type StringKeys<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

type UserStrings = StringKeys<User>;
// { name: string }

Filtering with never

// Remove null and undefined from property values
type NonNullable2<T> = {
  [K in keyof T]: T[K] extends null | undefined ? never : T[K];
};

// Pick only function properties
type FunctionProperties<T> = {
  [K in keyof T as T[K] extends Function ? K : never]: T[K];
};

interface Service {
  name: string;
  init(): void;
  process(data: string): string;
  destroy(): void;
  version: number;
}

type ServiceMethods = FunctionProperties<Service>;
// { init: () => void; process: (data: string) => string; destroy: () => void }

Practical Mapped Types

Practical Mapped Types

Deep Partial

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object
    ? DeepPartial<T[K]>
    : T[K];
};

interface Config {
  database: {
    host: string;
    port: number;
    credentials: {
      user: string;
      password: string;
    };
  };
  cache: {
    ttl: number;
    maxEntries: number;
  };
}

// All nested properties are optional
function updateConfig(patch: DeepPartial<Config>): void {
  // ...
}

updateConfig({
  database: {
    port: 5432
  }
});

PickByType

type PickByType<T, ValueType> = {
  [K in keyof T as T[K] extends ValueType ? K : never]: T[K];
};

interface Mixed {
  name: string;
  age: number;
  email: string;
  active: boolean;
  score: number;
}

type StringProps = PickByType<Mixed, string>;
// { name: string; email: string }

type NumberProps = PickByType<Mixed, number>;
// { age: number; score: number }

OmitByType

type OmitByType<T, ValueType> = {
  [K in keyof T as T[K] extends ValueType ? never : K]: T[K];
};

type NoStrings = OmitByType<Mixed, string>;
// { age: number; active: boolean; score: number }

Template Literal Mapped Types

Template Literal Types with Mapped Types

Event Handler Types

type EventConfig = {
  click: { x: number; y: number };
  hover: { element: string };
  focus: { target: HTMLElement };
};

// Generate handler types
type Handlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}`]: (event: T[K]) => void;
};

type EventHandlers = Handlers<EventConfig>;
// {
//   onClick: (event: { x: number; y: number }) => void;
//   onHover: (event: { element: string }) => void;
//   onFocus: (event: { target: HTMLElement }) => void;
// }

API Endpoints

type Route = '/users' | '/posts' | '/comments';

type ApiMethods = 'get' | 'post' | 'put' | 'delete';

type ApiClient = {
  [M in ApiMethods]: {
    [R in Route]: (data?: unknown) => Promise<unknown>;
  };
};

// Or flattened:
type ApiClient2 = {
  [K in `${ApiMethods}_${Route}`]: (data?: unknown) => Promise<unknown>;
};
// get_users, get_posts, post_users, post_posts, etc.

Best Practices

  • Use mapped types to derive types from existing interfaces
  • Combine with conditional types for filtering
  • Use key remapping to transform property names
  • Prefer mapped types over manual type definitions