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;
Комментарии