-
-
Notifications
You must be signed in to change notification settings - Fork 679
Expand file tree
/
Copy pathwords.d.ts
More file actions
148 lines (121 loc) · 5.07 KB
/
words.d.ts
File metadata and controls
148 lines (121 loc) · 5.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import type {
ApplyDefaultOptions,
AsciiPunctuation,
IsNumeric,
WordSeparators,
} from './internal/index.d.ts';
import type {IsLowercase} from './is-lowercase.d.ts';
import type {IsUppercase} from './is-uppercase.d.ts';
type SkipEmptyWord<Word extends string> = Word extends '' ? [] : [Word];
type RemoveLastCharacter<
Sentence extends string,
Character extends string,
> = Sentence extends `${infer LeftSide}${Character}`
? SkipEmptyWord<LeftSide>
: never;
/**
Words options.
@see {@link Words}
*/
export type WordsOptions = {
/**
Split on numeric sequence.
@default true
@example
```
import type {Words} from 'type-fest';
type Example1 = Words<'p2pNetwork', {splitOnNumbers: true}>;
//=> ['p', '2', 'p', 'Network']
type Example2 = Words<'p2pNetwork', {splitOnNumbers: false}>;
//=> ['p2p', 'Network']
```
*/
splitOnNumbers?: boolean;
/**
Split on {@link AsciiPunctuation | punctuation characters} (e.g., `#`, `&`, `*`, `:`, `?`, `@`, `~`).
@example
```
import type {Words} from 'type-fest';
type Example1 = Words<'hello:world', {splitOnPunctuation: true}>;
//=> ['hello', 'world']
type Example2 = Words<'hello:world', {splitOnPunctuation: false}>;
//=> ['hello', ':world']
```
*/
splitOnPunctuation?: boolean;
};
export type _DefaultWordsOptions = {
splitOnNumbers: true;
splitOnPunctuation: false;
};
/**
Split a string (almost) like Lodash's `_.words()` function.
- Split on each word that begins with a capital letter.
- Split on each {@link WordSeparators}.
- Split on each {@link AsciiPunctuation} (if {@link WordsOptions.splitOnPunctuation} is enabled).
- Split on numeric sequence.
@example
```
import type {Words} from 'type-fest';
type Words0 = Words<'helloWorld'>;
//=> ['hello', 'World']
type Words1 = Words<'helloWORLD'>;
//=> ['hello', 'WORLD']
type Words2 = Words<'hello-world'>;
//=> ['hello', 'world']
type Words3 = Words<'--hello the_world'>;
//=> ['hello', 'the', 'world']
type Words4 = Words<'lifeIs42'>;
//=> ['life', 'Is', '42']
type Words5 = Words<'p2pNetwork', {splitOnNumbers: false}>;
//=> ['p2p', 'Network']
type Words6 = Words<'hello:world', {splitOnPunctuation: true}>;
//=> ['hello', 'world']
type Words7 = Words<'hello:world', {splitOnPunctuation: false}>;
//=> ['hello', ':world']
type Words8 = Words<'hello::world', {splitOnPunctuation: true}>;
//=> ['hello', 'world']
```
@category Change case
@category Template literal
*/
export type Words<Sentence extends string, Options extends WordsOptions = {}> = WordsImplementation<Sentence, ApplyDefaultOptions<WordsOptions, _DefaultWordsOptions, Options>>;
type WordsImplementation<
Sentence extends string,
Options extends Required<WordsOptions>,
LastCharacter extends string = '',
CurrentWord extends string = '',
> = Sentence extends `${infer FirstCharacter}${infer RemainingCharacters}`
? FirstCharacter extends WordSeparators | (Options['splitOnPunctuation'] extends true ? AsciiPunctuation : never)
// Skip word separator
? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options>]
: LastCharacter extends ''
// Fist char of word
? WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>
// Case change: non-numeric to numeric
: [false, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
? Options['splitOnNumbers'] extends true
// Split on number: push word
? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>]
// No split on number: concat word
: WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
// Case change: numeric to non-numeric
: [true, false] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
? Options['splitOnNumbers'] extends true
// Split on number: push word
? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>]
// No split on number: concat word
: WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
// No case change: concat word
: [true, true] extends [IsNumeric<LastCharacter>, IsNumeric<FirstCharacter>]
? WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
// Case change: lower to upper, push word
: [true, true] extends [IsLowercase<LastCharacter>, IsUppercase<FirstCharacter>]
? [...SkipEmptyWord<CurrentWord>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, FirstCharacter>]
// Case change: upper to lower, brings back the last character, push word
: [true, true] extends [IsUppercase<LastCharacter>, IsLowercase<FirstCharacter>]
? [...RemoveLastCharacter<CurrentWord, LastCharacter>, ...WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${LastCharacter}${FirstCharacter}`>]
// No case change: concat word
: WordsImplementation<RemainingCharacters, Options, FirstCharacter, `${CurrentWord}${FirstCharacter}`>
: [...SkipEmptyWord<CurrentWord>];
export {};