TypeScript: реализуйте тип `SnakeCase`, который глубоко преобразует ключи объекта из `camelCase` в `snake_case`, сохраняя type safety.

На уровне типов преобразуйте имя ключа шаблонным строковым типом и рекурсивно примените key remapping к вложенным объектам, отдельно обрабатывая массивы.

Короткий ответ

Что ответить на собеседовании

На уровне типов преобразуйте имя ключа шаблонным строковым типом и рекурсивно примените key remapping к вложенным объектам, отдельно обрабатывая массивы.

Подробный разбор

Ответ с пояснениями

На уровне типов преобразуйте имя ключа шаблонным строковым типом и рекурсивно примените key remapping к вложенным объектам, отдельно обрабатывая массивы.

Пример рассчитан на JSON-подобные данные и lowerCamelCase с ASCII-буквами; последовательные заглавные буквы разбиваются по одной.

type SnakeName<S extends string> =
  S extends `${infer H}${infer R}`
    ? `${H extends Lowercase<H> ? H : `_${Lowercase<H>}`}${SnakeName<R>}`
    : S;

type SnakeCase<T> =
  T extends readonly unknown[]
    ? { [K in keyof T]: SnakeCase<T[K]> }
    : T extends object
      ? { [K in keyof T as K extends string ? SnakeName<K> : K]: SnakeCase<T[K]> }
      : T;

type Example = SnakeCase<{ userName: string; items: { createdAt: string }[] }>;
// { user_name: string; items: { created_at: string }[] }

Тип не переименовывает реальные ключи во время выполнения. Для Date, функций, аббревиатур и коллизий вроде userName/user_name нужен отдельный контракт; глубина вычисления ограничена компилятором.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее