На уровне типов преобразуйте имя ключа шаблонным строковым типом и рекурсивно примените key remapping к вложенным объектам, отдельно обрабатывая массивы.
TypeScript: реализуйте тип `SnakeCase`, который глубоко преобразует ключи объекта из `camelCase` в `snake_case`, сохраняя type safety.
На уровне типов преобразуйте имя ключа шаблонным строковым типом и рекурсивно примените 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 нужен отдельный контракт; глубина вычисления ограничена компилятором.