1- import type { IsLowerCase , IsNumeric , IsUpperCase , WordSeparators } from './internal' ;
1+ import type {
2+ IsLowerCase ,
3+ IsNumeric ,
4+ IsUpperCase ,
5+ WordSeparators ,
6+ } from './internal' ;
27
38type SkipEmptyWord < Word extends string > = Word extends '' ? [ ] : [ Word ] ;
49
5- type RemoveLastCharacter < Sentence extends string , Character extends string > = Sentence extends `${infer LeftSide } ${Character } `
10+ type RemoveLastCharacter <
11+ Sentence extends string ,
12+ Character extends string ,
13+ > = Sentence extends `${infer LeftSide } ${Character } `
614 ? SkipEmptyWord < LeftSide >
715 : never ;
816
17+ /**
18+ Words options.
19+
20+ @see {@link Words }
21+ */
22+ export type WordsOptions = {
23+ /**
24+ Split on numeric sequence.
25+
26+ @default true
27+
28+ @example
29+ ```
30+ type Example1 = Words<'p2pNetwork', {splitOnNumbers: true}>;
31+ //=> ["p", "2", "p", "Network"]
32+
33+ type Example2 = Words<'p2pNetwork', {splitOnNumbers: false}>;
34+ //=> ["p2p", "Network"]
35+ ```
36+ */
37+ splitOnNumbers ?: boolean ;
38+ } ;
39+
40+ type DefaultOptions = {
41+ splitOnNumbers : true ;
42+ } ;
43+
944/**
1045Split a string (almost) like Lodash's `_.words()` function.
1146
@@ -31,37 +66,53 @@ type Words3 = Words<'--hello the_world'>;
3166
3267type Words4 = Words<'lifeIs42'>;
3368//=> ['life', 'Is', '42']
69+
70+ type Words5 = Words<'p2pNetwork', {splitOnNumbers: false}>;
71+ //=> ['p2p', 'Network']
3472```
3573
3674@category Change case
3775@category Template literal
3876*/
39- export type Words <
77+ export type Words < Sentence extends string , Options extends WordsOptions = { } > = WordsImplementation < Sentence , {
78+ splitOnNumbers : Options [ 'splitOnNumbers' ] extends boolean ? Options [ 'splitOnNumbers' ] : DefaultOptions [ 'splitOnNumbers' ] ;
79+ } > ;
80+
81+ type WordsImplementation <
4082 Sentence extends string ,
83+ Options extends Required < WordsOptions > ,
4184 LastCharacter extends string = '' ,
4285 CurrentWord extends string = '' ,
4386> = Sentence extends `${infer FirstCharacter } ${infer RemainingCharacters } `
4487 ? FirstCharacter extends WordSeparators
4588 // Skip word separator
46- ? [ ...SkipEmptyWord < CurrentWord > , ...Words < RemainingCharacters > ]
89+ ? [ ...SkipEmptyWord < CurrentWord > , ...WordsImplementation < RemainingCharacters , Options > ]
4790 : LastCharacter extends ''
4891 // Fist char of word
49- ? Words < RemainingCharacters , FirstCharacter , FirstCharacter >
50- // Case change: non-numeric to numeric, push word
92+ ? WordsImplementation < RemainingCharacters , Options , FirstCharacter , FirstCharacter >
93+ // Case change: non-numeric to numeric
5194 : [ false , true ] extends [ IsNumeric < LastCharacter > , IsNumeric < FirstCharacter > ]
52- ? [ ...SkipEmptyWord < CurrentWord > , ...Words < RemainingCharacters , FirstCharacter , FirstCharacter > ]
53- // Case change: numeric to non-numeric, push word
95+ ? Options [ 'splitOnNumbers' ] extends true
96+ // Split on number: push word
97+ ? [ ...SkipEmptyWord < CurrentWord > , ...WordsImplementation < RemainingCharacters , Options , FirstCharacter , FirstCharacter > ]
98+ // No split on number: concat word
99+ : WordsImplementation < RemainingCharacters , Options , FirstCharacter , `${CurrentWord } ${FirstCharacter } `>
100+ // Case change: numeric to non-numeric
54101 : [ true , false ] extends [ IsNumeric < LastCharacter > , IsNumeric < FirstCharacter > ]
55- ? [ ...SkipEmptyWord < CurrentWord > , ...Words < RemainingCharacters , FirstCharacter , FirstCharacter > ]
102+ ? Options [ 'splitOnNumbers' ] extends true
103+ // Split on number: push word
104+ ? [ ...SkipEmptyWord < CurrentWord > , ...WordsImplementation < RemainingCharacters , Options , FirstCharacter , FirstCharacter > ]
105+ // No split on number: concat word
106+ : WordsImplementation < RemainingCharacters , Options , FirstCharacter , `${CurrentWord } ${FirstCharacter } `>
56107 // No case change: concat word
57108 : [ true , true ] extends [ IsNumeric < LastCharacter > , IsNumeric < FirstCharacter > ]
58- ? Words < RemainingCharacters , FirstCharacter , `${CurrentWord } ${FirstCharacter } `>
109+ ? WordsImplementation < RemainingCharacters , Options , FirstCharacter , `${CurrentWord } ${FirstCharacter } `>
59110 // Case change: lower to upper, push word
60111 : [ true , true ] extends [ IsLowerCase < LastCharacter > , IsUpperCase < FirstCharacter > ]
61- ? [ ...SkipEmptyWord < CurrentWord > , ...Words < RemainingCharacters , FirstCharacter , FirstCharacter > ]
112+ ? [ ...SkipEmptyWord < CurrentWord > , ...WordsImplementation < RemainingCharacters , Options , FirstCharacter , FirstCharacter > ]
62113 // Case change: upper to lower, brings back the last character, push word
63114 : [ true , true ] extends [ IsUpperCase < LastCharacter > , IsLowerCase < FirstCharacter > ]
64- ? [ ...RemoveLastCharacter < CurrentWord , LastCharacter > , ...Words < RemainingCharacters , FirstCharacter , `${LastCharacter } ${FirstCharacter } `> ]
115+ ? [ ...RemoveLastCharacter < CurrentWord , LastCharacter > , ...WordsImplementation < RemainingCharacters , Options , FirstCharacter , `${LastCharacter } ${FirstCharacter } `> ]
65116 // No case change: concat word
66- : Words < RemainingCharacters , FirstCharacter , `${CurrentWord } ${FirstCharacter } `>
117+ : WordsImplementation < RemainingCharacters , Options , FirstCharacter , `${CurrentWord } ${FirstCharacter } `>
67118 : [ ...SkipEmptyWord < CurrentWord > ] ;
0 commit comments