|
| 1 | +type Assert<T extends true> = T |
| 2 | +type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false |
| 3 | + |
| 4 | +type Splitter = '-' | '_' | '/' | '.' |
| 5 | +type FirstOfString<S extends string> = S extends `${infer F}${string}` ? F : never |
| 6 | +type RemoveFirstOfString<S extends string> = S extends `${string}${infer R}` ? R : never |
| 7 | +type IsUpper<S extends string> = S extends Uppercase<S> ? true : false |
| 8 | +type IsLower<S extends string> = S extends Lowercase<S> ? true : false |
| 9 | +type SameLetterCase<X extends string, Y extends string> = IsUpper<X> extends IsUpper<Y> ? true : IsLower<X> extends IsLower<Y> ? true : false |
| 10 | +type CapitalizedWords<T extends readonly string[], Acc extends string = ''> = |
| 11 | + T extends readonly [infer F extends string, ...infer R extends string[]] |
| 12 | + ? CapitalizedWords<R, `${Acc}${Capitalize<F>}`> |
| 13 | + : Acc |
| 14 | +type JoinLowercaseWords<T extends readonly string[], Joiner extends string, Acc extends string = ''> = |
| 15 | + T extends readonly [infer F extends string, ...infer R extends string[]] |
| 16 | + ? Acc extends '' |
| 17 | + ? JoinLowercaseWords<R, Joiner, `${Acc}${Lowercase<F>}`> |
| 18 | + : JoinLowercaseWords<R, Joiner, `${Acc}${Joiner}${Lowercase<F>}`> |
| 19 | + : Acc |
| 20 | + |
| 21 | +type LastOfArray<T extends any[]> = T extends [...any, infer R] ? R : never |
| 22 | +type RemoveLastOfArray<T extends any[]> = T extends [...infer F, any] ? F : never |
| 23 | + |
| 24 | +export type SplitByCase<T, Sep extends string = Splitter, Acc extends unknown[] = []> = |
| 25 | + string extends Sep |
| 26 | + ? string[] |
| 27 | + : T extends `${infer F}${infer R}` |
| 28 | + ? [LastOfArray<Acc>] extends [never] |
| 29 | + ? SplitByCase<R, Sep, [F]> |
| 30 | + : LastOfArray<Acc> extends string |
| 31 | + ? R extends '' |
| 32 | + ? SplitByCase<R, Sep, [...RemoveLastOfArray<Acc>, `${LastOfArray<Acc>}${F}`]> |
| 33 | + : SameLetterCase<F, FirstOfString<R>> extends true |
| 34 | + ? F extends Sep |
| 35 | + ? FirstOfString<R> extends Sep |
| 36 | + ? SplitByCase<R, Sep, [...Acc, '']> |
| 37 | + : IsUpper<FirstOfString<R>> extends true |
| 38 | + ? SplitByCase<RemoveFirstOfString<R>, Sep, [...Acc, FirstOfString<R>]> |
| 39 | + : SplitByCase<R, Sep, [...Acc, '']> |
| 40 | + : SplitByCase<R, Sep, [...RemoveLastOfArray<Acc>, `${LastOfArray<Acc>}${F}`]> |
| 41 | + : IsLower<F> extends true |
| 42 | + ? SplitByCase<RemoveFirstOfString<R>, Sep, [...RemoveLastOfArray<Acc>, `${LastOfArray<Acc>}${F}`, FirstOfString<R>]> |
| 43 | + : SplitByCase<R, Sep, [...Acc, F]> |
| 44 | + : never |
| 45 | + : Acc extends [] |
| 46 | + ? T extends '' ? [] : string[] |
| 47 | + : Acc |
| 48 | + |
| 49 | +export type PascalCase<T> = |
| 50 | + string extends T |
| 51 | + ? string |
| 52 | + : string[] extends T |
| 53 | + ? string |
| 54 | + : T extends string |
| 55 | + ? SplitByCase<T> extends readonly string[] |
| 56 | + ? CapitalizedWords<SplitByCase<T>> |
| 57 | + : never |
| 58 | + : T extends readonly string[] |
| 59 | + ? CapitalizedWords<T> |
| 60 | + : never |
| 61 | + |
| 62 | +export type CamelCase<T> = |
| 63 | + string extends T |
| 64 | + ? string |
| 65 | + : string[] extends T |
| 66 | + ? string |
| 67 | + : Uncapitalize<PascalCase<T>> |
| 68 | + |
| 69 | +export type JoinByCase<T, Joiner extends string> = |
| 70 | + string extends T |
| 71 | + ? string |
| 72 | + : string[] extends T |
| 73 | + ? string |
| 74 | + : T extends string |
| 75 | + ? SplitByCase<T> extends readonly string[] |
| 76 | + ? JoinLowercaseWords<SplitByCase<T>, Joiner> |
| 77 | + : never |
| 78 | + : T extends readonly string[] |
| 79 | + ? JoinLowercaseWords<T, Joiner> |
| 80 | + : never |
| 81 | + |
| 82 | +/* eslint-disable @typescript-eslint/no-unused-vars */ |
| 83 | +type tests = [ |
| 84 | + // SplitByCase |
| 85 | + Assert<Equal<SplitByCase<string>, string[]>>, |
| 86 | + // default splitters |
| 87 | + Assert<Equal<SplitByCase<''>, []>>, |
| 88 | + Assert<Equal<SplitByCase<'foo'>, ['foo']>>, |
| 89 | + Assert<Equal<SplitByCase<'foo_bar-baz/qux'>, ['foo', 'bar', 'baz', 'qux']>>, |
| 90 | + Assert<Equal<SplitByCase<'foo--bar-Baz'>, ['foo', '', 'bar', 'Baz']>>, |
| 91 | + Assert<Equal<SplitByCase<'foo123-bar'>, ['foo123', 'bar']>>, |
| 92 | + Assert<Equal<SplitByCase<'fooBar'>, ['foo', 'Bar']>>, |
| 93 | + Assert<Equal<SplitByCase<'fooBARBaz'>, ['foo', 'BAR', 'Baz']>>, |
| 94 | + Assert<Equal<SplitByCase<'FOOBar'>, ['FOO', 'Bar']>>, |
| 95 | + Assert<Equal<SplitByCase<'ALink'>, ['A', 'Link']>>, |
| 96 | + // custom splitters |
| 97 | + Assert<Equal<SplitByCase<'foo\\Bar.fuzz-FIZz', '\\' | '.' | '-'>, ['foo', 'Bar', 'fuzz', 'FI', 'Zz']>>, |
| 98 | + |
| 99 | + // PascalCase |
| 100 | + Assert<Equal<PascalCase<string>, string>>, |
| 101 | + Assert<Equal<PascalCase<string[]>, string>>, |
| 102 | + // string |
| 103 | + Assert<Equal<PascalCase<''>, ''>>, |
| 104 | + Assert<Equal<PascalCase<'foo'>, 'Foo'>>, |
| 105 | + Assert<Equal<PascalCase<'foo-bAr'>, 'FooBAr'>>, |
| 106 | + Assert<Equal<PascalCase<'FooBARb'>, 'FooBARb'>>, |
| 107 | + Assert<Equal<PascalCase<'foo_bar-baz/qux'>, 'FooBarBazQux'>>, |
| 108 | + Assert<Equal<PascalCase<'foo--bar-Baz'>, 'FooBarBaz'>>, |
| 109 | + // array |
| 110 | + Assert<Equal<PascalCase<['foo', 'Bar']>, 'FooBar'>>, |
| 111 | + Assert<Equal<PascalCase<['foo', 'Bar', 'fuzz', 'FI', 'Zz']>, 'FooBarFuzzFIZz'>>, |
| 112 | + |
| 113 | + // CamelCase |
| 114 | + Assert<Equal<CamelCase<string>, string>>, |
| 115 | + Assert<Equal<CamelCase<string[]>, string>>, |
| 116 | + // string |
| 117 | + Assert<Equal<CamelCase<''>, ''>>, |
| 118 | + Assert<Equal<CamelCase<'foo'>, 'foo'>>, |
| 119 | + Assert<Equal<CamelCase<'FooBARb'>, 'fooBARb'>>, |
| 120 | + Assert<Equal<CamelCase<'foo_bar-baz/qux'>, 'fooBarBazQux'>>, |
| 121 | + // array |
| 122 | + Assert<Equal<CamelCase<['Foo', 'Bar']>, 'fooBar'>>, |
| 123 | + |
| 124 | + // JoinByCase |
| 125 | + Assert<Equal<JoinByCase<string, '-'>, string>>, |
| 126 | + Assert<Equal<JoinByCase<string[], '-'>, string>>, |
| 127 | + // string |
| 128 | + Assert<Equal<JoinByCase<'', '-'>, ''>>, |
| 129 | + Assert<Equal<JoinByCase<'foo', '-'>, 'foo'>>, |
| 130 | + Assert<Equal<JoinByCase<'FooBARb', '-'>, 'foo-ba-rb'>>, |
| 131 | + Assert<Equal<JoinByCase<'foo_bar-baz/qux', '-'>, 'foo-bar-baz-qux'>>, |
| 132 | + // array |
| 133 | + Assert<Equal<JoinByCase<['Foo', 'Bar'], '-'>, 'foo-bar'>>, |
| 134 | +]; |
| 135 | +/* eslint-enable @typescript-eslint/no-unused-vars */ |
0 commit comments