UNPKG

2.77 kBTypeScriptView Raw
1import type {IsLowerCase, IsNumeric, IsUpperCase, WordSeparators} from './internal';
2
3type SkipEmptyWord<Word extends string> = Word extends '' ? [] : [Word];
4
5type RemoveLastCharacter<Sentence extends string, Character extends string> = Sentence extends `${infer LeftSide}${Character}`
6 ? SkipEmptyWord<LeftSide>
7 : never;
8
9/**
10Split a string (almost) like Lodash's `_.words()` function.
11
12- Split on each word that begins with a capital letter.
13- Split on each {@link WordSeparators}.
14- Split on numeric sequence.
15
16@example
17```
18import type {Words} from 'type-fest';
19
20type Words0 = Words<'helloWorld'>;
21//=> ['hello', 'World']
22
23type Words1 = Words<'helloWORLD'>;
24//=> ['hello', 'WORLD']
25
26type Words2 = Words<'hello-world'>;
27//=> ['hello', 'world']
28
29type Words3 = Words<'--hello the_world'>;
30//=> ['hello', 'the', 'world']
31
32type Words4 = Words<'lifeIs42'>;
33//=> ['life', 'Is', '42']
34```
35
36@category Change case
37@category Template literal
38*/
39export type Words<
40 Sentence extends string,
41 LastCharacter extends string = '',
42 CurrentWord extends string = '',
43> = Sentence extends `${infer FirstCharacter}${infer RemainingCharacters}`
44 ? FirstCharacter extends WordSeparators
45 // Skip word separator
46 ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters>]
47 : LastCharacter extends ''
48 // Fist char of word
49 ? Words<RemainingCharacters, FirstCharacter, FirstCharacter>
50 // Case change: non-numeric to numeric, push word
51 : [false, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
52 ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters, FirstCharacter, FirstCharacter>]
53 // Case change: numeric to non-numeric, push word
54 : [true, false] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
55 ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters, FirstCharacter, FirstCharacter>]
56 // No case change: concat word
57 : [true, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
58 ? Words<RemainingCharacters, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
59 // Case change: lower to upper, push word
60 : [true, true] extends [IsLowerCase<LastCharacter>, IsUpperCase<FirstCharacter>]
61 ? [...SkipEmptyWord<CurrentWord>, ...Words<RemainingCharacters, FirstCharacter, FirstCharacter>]
62 // Case change: upper to lower, brings back the last character, push word
63 : [true, true] extends [IsUpperCase<LastCharacter>, IsLowerCase<FirstCharacter>]
64 ? [...RemoveLastCharacter<CurrentWord, LastCharacter>, ...Words<RemainingCharacters, FirstCharacter, `${LastCharacter}${FirstCharacter}`>]
65 // No case change: concat word
66 : Words<RemainingCharacters, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
67 : [...SkipEmptyWord<CurrentWord>];