forked from sindresorhus/type-fest
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwords.d.ts
More file actions
118 lines (99 loc) · 4.09 KB
/
words.d.ts
File metadata and controls
118 lines (99 loc) · 4.09 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
import type {
IsLowerCase,
IsNumeric,
IsUpperCase,
WordSeparators,
} from './internal';
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
```
type Example1 = Words<'p2pNetwork', {splitOnNumbers: true}>;
//=> ["p", "2", "p", "Network"]
type Example2 = Words<'p2pNetwork', {splitOnNumbers: false}>;
//=> ["p2p", "Network"]
```
*/
splitOnNumbers?: boolean;
};
type DefaultOptions = {
splitOnNumbers: true;
};
/**
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 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']
```
@category Change case
@category Template literal
*/
export type Words<Sentence extends string, Options extends WordsOptions = {}> = WordsImplementation<Sentence, {
splitOnNumbers: Options['splitOnNumbers'] extends boolean ? Options['splitOnNumbers'] : DefaultOptions['splitOnNumbers'];
}>;
type WordsImplementation<
Sentence extends string,
Options extends Required<WordsOptions>,
LastCharacter extends string = '',
CurrentWord extends string = '',
> = Sentence extends `${infer FirstCharacter}${infer RemainingCharacters}`
? FirstCharacter extends WordSeparators
// 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>];