KebabCase
Проблема
Привести строку к формату kebab-case. Например:
type kebabCase = KebabCase<"FooBarBaz">; // expected "foo-bar-baz"
Решение
Эта проблема имеет много общего с проблемой CamelCase.
Начнём с вывода типов. Узнаем первую букву строки и остальную часть (хвост).
type KebabCase<S> = S extends `${infer C}${infer T}` ? never : never;
Когда попадаем в ситуацию, где нету шаблона для “первая буква и хвост”, это значит что строка закончилась. Поэтому, возвращаем входной параметр без изменений.
type KebabCase<S> = S extends `${infer C}${infer T}` ? never : S;
Но, если шаблон сработал, то здесь два случая. Первый случай, когда наш хвост не
имеет заглавную букву, а второй - когда имеет. Чтобы это проверить, используем
встроенный тип Uncapitalize.
type KebabCase<S> = S extends `${infer C}${infer T}`
  ? T extends Uncapitalize<T>
    ? never
    : never
  : S;
Что нужно сделать, если хвост не имеет заглавной буквы? Это значит, что у нас
может быть Foo или foo. А значит, делаем первую букву не заглавной, а
остальное без изменений. И не забываем, что применяем этот тип рекурсивно, так
как обрабатываем следующие части строки.
type KebabCase<S> = S extends `${infer C}${infer T}`
  ? T extends Uncapitalize<T>
    ? `${Uncapitalize<C>}${KebabCase<T>}`
    : never
  : S;
Второй случай, когда хвост содержит заглавную букву. Например, fooBar. Всё ещё
делаем первую букву не заглавной, но теперь, ещё и добавляем дефис и продолжаем
рекурсивно обрабатывать строку. Нам не нужно применять Uncapitalize к хвосту,
так как он будет применён на следующих итерациях, когда он станет первой буквой
подстроки.
type KebabCase<S> = S extends `${infer C}${infer T}`
  ? T extends Uncapitalize<T>
    ? `${Uncapitalize<C>}${KebabCase<T>}`
    : `${Uncapitalize<C>}-${KebabCase<T>}`
  : S;
Комментарии