Код IT
← Каталог

Дженерики в TypeScript — Пути во вложенном объекте: `Path` и `PathType`

Фрагмент из «Дженерики в TypeScript»: Пути во вложенном объекте: `Path` и `PathType`.

TypeScript main.ts
type Path<T> = T extends object
  ? {
      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;
    }[keyof T]
  : never;

type PathType<T, P extends Path<T>> = P extends `${infer Key}.${infer Rest}`
  ? Key extends keyof T
    ? Rest extends Path<T[Key]>
      ? PathType<T[Key], Rest>
      : never
    : never
  : P extends keyof T
    ? T[P]
    : never;

interface AppState {
  user: {
    id: string;
    preferences: {
      theme: "light" | "dark";
      lang: "en" | "es";
    };
  };
  ui: { sidebarOpen: boolean };
}

type AppStatePath = Path<AppState>;
// "user" | "ui" | "user.id" | "user.preferences" | "user.preferences.theme" | …

type Theme = PathType<AppState, "user.preferences.theme">; // "light" | "dark"
type Sidebar = PathType<AppState, "ui.sidebarOpen">; // boolean
type Path<T> = T extends object
  ? {
      [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;
    }[keyof T]
  : never;

type PathType<T, P extends Path<T>> = P extends `${infer Key}.${infer Rest}`
  ? Key extends keyof T
    ? Rest extends Path<T[Key]>
      ? PathType<T[Key], Rest>
      : never
    : never
  : P extends keyof T
    ? T[P]
    : never;

interface AppState {
  user: {
    id: string;
    preferences: {
      theme: "light" | "dark";
      lang: "en" | "es";
    };
  };
  ui: { sidebarOpen: boolean };
}

type AppStatePath = Path<AppState>;
// "user" | "ui" | "user.id" | "user.preferences" | "user.preferences.theme" | …

type Theme = PathType<AppState, "user.preferences.theme">; // "light" | "dark"
type Sidebar = PathType<AppState, "ui.sidebarOpen">; // boolean