Kebabcase

挑战

Replace the camelCase or PascalCase string with kebab-case.

FooBarBaz -> foo-bar-baz

For example

type FooBarBaz = KebabCase<'FooBarBaz'>;
const foobarbaz: FooBarBaz = 'foo-bar-baz';
 
type DoNothing = KebabCase<'do-nothing'>;
const doNothing: DoNothing = 'do-nothing';

解答

像很多字符串题目一样,我们还是从推断首字母和剩下的字符串开始。

type KebabCase<S> = S extends `${infer C}${infer T}` ? never : never;

一旦未匹配到就意味着我们全部转换完成。我们只需要原样返回输入的字符串。

type KebabCase<S> = S extends `${infer C}${infer T}` ? never : S;

但是,一旦匹配到我们就需要处理 2 种情况。一种情况是没有首字母大写的尾部,一种情 况是有。为了检测这个,我们可以用内置类型 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<C> 始终会使它变小写。

type KebabCase<S> = S extends `${infer C}${infer T}`
  ? T extends Uncapitalize<T>
    ? `${Uncapitalize<C>}${KebabCase<T>}`
    : `${Uncapitalize<C>}-${KebabCase<T>}`
  : S;

参考链接