@planet-matrix/mobius-model 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +30 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +22 -4
- package/package.json +3 -3
- package/scripts/build.ts +4 -4
- package/src/basic/README.md +144 -0
- package/src/basic/array.ts +872 -0
- package/src/basic/bigint.ts +114 -0
- package/src/basic/boolean.ts +180 -0
- package/src/basic/enhance.ts +10 -0
- package/src/basic/error.ts +51 -0
- package/src/basic/function.ts +453 -0
- package/src/basic/helper.ts +276 -0
- package/src/basic/index.ts +17 -0
- package/src/basic/is.ts +320 -0
- package/src/basic/number.ts +178 -0
- package/src/basic/object.ts +140 -0
- package/src/basic/promise.ts +464 -0
- package/src/basic/regexp.ts +7 -0
- package/src/basic/stream.ts +140 -0
- package/src/basic/string.ts +308 -0
- package/src/basic/symbol.ts +164 -0
- package/src/basic/temporal.ts +224 -0
- package/src/encoding/README.md +105 -0
- package/src/encoding/base64.ts +98 -0
- package/src/encoding/index.ts +1 -0
- package/src/index.ts +4 -0
- package/src/random/README.md +109 -0
- package/src/random/index.ts +1 -0
- package/src/random/uuid.ts +103 -0
- package/src/type/README.md +330 -0
- package/src/type/array.ts +5 -0
- package/src/type/boolean.ts +471 -0
- package/src/type/class.ts +419 -0
- package/src/type/function.ts +1519 -0
- package/src/type/helper.ts +135 -0
- package/src/type/index.ts +14 -0
- package/src/type/intersection.ts +93 -0
- package/src/type/is.ts +247 -0
- package/src/type/iteration.ts +233 -0
- package/src/type/number.ts +732 -0
- package/src/type/object.ts +788 -0
- package/src/type/path.ts +73 -0
- package/src/type/string.ts +1004 -0
- package/src/type/tuple.ts +2424 -0
- package/src/type/union.ts +108 -0
- package/tests/unit/basic/array.spec.ts +290 -0
- package/tests/unit/basic/bigint.spec.ts +50 -0
- package/tests/unit/basic/boolean.spec.ts +74 -0
- package/tests/unit/basic/error.spec.ts +32 -0
- package/tests/unit/basic/function.spec.ts +175 -0
- package/tests/unit/basic/helper.spec.ts +118 -0
- package/tests/unit/basic/number.spec.ts +74 -0
- package/tests/unit/basic/object.spec.ts +46 -0
- package/tests/unit/basic/promise.spec.ts +232 -0
- package/tests/unit/basic/regexp.spec.ts +11 -0
- package/tests/unit/basic/stream.spec.ts +120 -0
- package/tests/unit/basic/string.spec.ts +74 -0
- package/tests/unit/basic/symbol.spec.ts +72 -0
- package/tests/unit/basic/temporal.spec.ts +78 -0
- package/tests/unit/encoding/base64.spec.ts +40 -0
- package/tests/unit/random/uuid.spec.ts +37 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/reactor/index.d.ts +0 -3
- package/dist/reactor/index.d.ts.map +0 -1
- package/dist/reactor/reactor-core/flags.d.ts +0 -99
- package/dist/reactor/reactor-core/flags.d.ts.map +0 -1
- package/dist/reactor/reactor-core/index.d.ts +0 -4
- package/dist/reactor/reactor-core/index.d.ts.map +0 -1
- package/dist/reactor/reactor-core/primitive.d.ts +0 -276
- package/dist/reactor/reactor-core/primitive.d.ts.map +0 -1
- package/dist/reactor/reactor-core/reactive-system.d.ts +0 -241
- package/dist/reactor/reactor-core/reactive-system.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/branch.d.ts +0 -19
- package/dist/reactor/reactor-operators/branch.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/convert.d.ts +0 -30
- package/dist/reactor/reactor-operators/convert.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/create.d.ts +0 -26
- package/dist/reactor/reactor-operators/create.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/filter.d.ts +0 -269
- package/dist/reactor/reactor-operators/filter.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/index.d.ts +0 -8
- package/dist/reactor/reactor-operators/index.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/join.d.ts +0 -48
- package/dist/reactor/reactor-operators/join.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/map.d.ts +0 -165
- package/dist/reactor/reactor-operators/map.d.ts.map +0 -1
- package/dist/reactor/reactor-operators/utility.d.ts +0 -48
- package/dist/reactor/reactor-operators/utility.d.ts.map +0 -1
|
@@ -0,0 +1,1004 @@
|
|
|
1
|
+
// oxlint-disable ban-types
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Primitives
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
export type StringAutoCompletable = string & {}
|
|
8
|
+
/**
|
|
9
|
+
* @see {@link https://x.com/mattpocockuk/status/1822917991232569660}
|
|
10
|
+
*/
|
|
11
|
+
export type StringWithAutoCompleteOptions<Options extends string> = Options | StringAutoCompletable
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Define the uppercase letter type.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```
|
|
18
|
+
* // Expect: 'A' | 'B' | ... | 'Z'
|
|
19
|
+
* type Example1 = UppercaseLetter
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export type UppercaseLetter = 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M' | 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Define the lowercase letter type.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```
|
|
29
|
+
* // Expect: 'a' | 'b' | ... | 'z'
|
|
30
|
+
* type Example1 = LowercaseLetter
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export type LowercaseLetter = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Define the digit character type.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```
|
|
40
|
+
* // Expect: '0' | '1' | ... | '9'
|
|
41
|
+
* type Example1 = DigitCharacter
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export type DigitCharacter = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Define the alphanumeric character type.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```
|
|
51
|
+
* // Expect: 'a' | 'b' | ... | 'z' | 'A' | 'B' | ... | 'Z' | '0' | '1' | ... | '9'
|
|
52
|
+
* type Example1 = Alphanumeric
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export type AlphanumericCharacter = UppercaseLetter | LowercaseLetter | DigitCharacter
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Validation
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if a string only contains digits.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```
|
|
66
|
+
* // Expect: true
|
|
67
|
+
* type Example1 = IsNumber<'123'>
|
|
68
|
+
* // Expect: false
|
|
69
|
+
* type Example2 = IsNumber<'12a'>
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export type StringIsNumber<S extends string> =
|
|
73
|
+
S extends ''
|
|
74
|
+
? false
|
|
75
|
+
: (
|
|
76
|
+
S extends `${infer First}${infer Rest}`
|
|
77
|
+
? (
|
|
78
|
+
First extends '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
|
|
79
|
+
? (Rest extends '' ? true : StringIsNumber<Rest>)
|
|
80
|
+
: false
|
|
81
|
+
)
|
|
82
|
+
: false
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if a string is all lowercase.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```
|
|
90
|
+
* // Expect: true
|
|
91
|
+
* type Example1 = StringIsLowerCase<'hello'>
|
|
92
|
+
* // Expect: false
|
|
93
|
+
* type Example2 = StringIsLowerCase<'Hello'>
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
export type StringIsLowerCase<S extends string> = S extends Lowercase<S> ? true : false
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Check if a string is all uppercase.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```
|
|
103
|
+
* // Expect: true
|
|
104
|
+
* type Example1 = StringIsUpperCase<'HELLO'>
|
|
105
|
+
* // Expect: false
|
|
106
|
+
* type Example2 = StringIsUpperCase<'Hello'>
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export type StringIsUpperCase<S extends string> = S extends Uppercase<S> ? true : false
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Check if a string only contains alphabetic characters.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```
|
|
116
|
+
* // Expect: true
|
|
117
|
+
* type Example1 = StringIsAlpha<'hello'>
|
|
118
|
+
* // Expect: false
|
|
119
|
+
* type Example2 = StringIsAlpha<'hello123'>
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
export type StringIsAlpha<S extends string> =
|
|
123
|
+
S extends ''
|
|
124
|
+
? false
|
|
125
|
+
: (
|
|
126
|
+
S extends `${infer First}${infer Rest}`
|
|
127
|
+
? (
|
|
128
|
+
First extends Uppercase<First>
|
|
129
|
+
? (
|
|
130
|
+
First extends Lowercase<First>
|
|
131
|
+
? false
|
|
132
|
+
: (Rest extends '' ? true : StringIsAlpha<Rest>)
|
|
133
|
+
)
|
|
134
|
+
: (Rest extends '' ? true : StringIsAlpha<Rest>)
|
|
135
|
+
)
|
|
136
|
+
: false
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check if a string only contains whitespace characters.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```
|
|
144
|
+
* // Expect: true
|
|
145
|
+
* type Example1 = StringIsWhitespace<' '>
|
|
146
|
+
* // Expect: false
|
|
147
|
+
* type Example2 = StringIsWhitespace<' a '>
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export type StringIsWhitespace<S extends string> =
|
|
151
|
+
S extends ''
|
|
152
|
+
? true
|
|
153
|
+
: (
|
|
154
|
+
S extends `${infer First}${infer Rest}`
|
|
155
|
+
? (
|
|
156
|
+
First extends ' ' | '\n' | '\t' | '\r'
|
|
157
|
+
? StringIsWhitespace<Rest>
|
|
158
|
+
: false
|
|
159
|
+
)
|
|
160
|
+
: false
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Check if a string is empty.
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```
|
|
168
|
+
* // Expect: true
|
|
169
|
+
* type Example1 = StringIsEmpty<''>
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export type StringIsEmpty<S extends string> = S extends '' ? true : false
|
|
173
|
+
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// Comparison
|
|
176
|
+
// ============================================================================
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check if two strings are equal.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```
|
|
183
|
+
* // Expect: true
|
|
184
|
+
* type Example1 = StringIsEqual<'hello', 'hello'>
|
|
185
|
+
* // Expect: false
|
|
186
|
+
* type Example2 = StringIsEqual<'hello', 'world'>
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export type StringIsEqual<S1 extends string, S2 extends string> =
|
|
190
|
+
S1 extends S2 ? (S2 extends S1 ? true : false) : false
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Check if a string starts with a prefix.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```
|
|
197
|
+
* // Expect: true
|
|
198
|
+
* type Example1 = StringStartsWith<'hello world', 'hello'>
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
export type StringStartsWith<S extends string, Prefix extends string> = S extends `${Prefix}${string}` ? true : false
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Check if a string ends with a suffix.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```
|
|
208
|
+
* // Expect: true
|
|
209
|
+
* type Example1 = StringEndsWith<'hello world', 'world'>
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export type StringEndsWith<S extends string, Suffix extends string> = S extends `${string}${Suffix}` ? true : false
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Check if a string includes a substring.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```
|
|
219
|
+
* // Expect: true
|
|
220
|
+
* type Example1 = StringIncludes<'hello world', 'lo wo'>
|
|
221
|
+
* ```
|
|
222
|
+
*/
|
|
223
|
+
export type StringIncludes<S extends string, Substring extends string> = S extends `${string}${Substring}${string}`
|
|
224
|
+
? true
|
|
225
|
+
: false
|
|
226
|
+
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Query
|
|
229
|
+
// ============================================================================
|
|
230
|
+
|
|
231
|
+
type InternalStringLength<S extends string, Acc extends readonly unknown[]> = S extends `${string}${infer Rest}`
|
|
232
|
+
? InternalStringLength<Rest, [...Acc, unknown]>
|
|
233
|
+
: Acc['length']
|
|
234
|
+
/**
|
|
235
|
+
* Get the length of a string literal type.
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* ```
|
|
239
|
+
* // Expect: 5
|
|
240
|
+
* type Example1 = StringLength<'hello'>
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
export type StringLength<S extends string> = InternalStringLength<S, []>
|
|
244
|
+
|
|
245
|
+
type InternalStringCount<S extends string, Sub extends string, Count extends readonly unknown[] = []> =
|
|
246
|
+
S extends `${infer _Start}${Sub}${infer Rest}`
|
|
247
|
+
? InternalStringCount<Rest, Sub, [...Count, unknown]>
|
|
248
|
+
: Count['length']
|
|
249
|
+
/**
|
|
250
|
+
* Count the number of occurrences of a substring.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* ```
|
|
254
|
+
* // Expect: 2
|
|
255
|
+
* type Example1 = StringCount<'hello world', 'l'>
|
|
256
|
+
* // Expect: 0
|
|
257
|
+
* type Example2 = StringCount<'hello', 'x'>
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
export type StringCount<S extends string, Sub extends string> =
|
|
261
|
+
Sub extends '' ? 0 : InternalStringCount<S, Sub>
|
|
262
|
+
|
|
263
|
+
type InternalStringIndexOf<S extends string, Sub extends string, Index extends readonly unknown[] = []> =
|
|
264
|
+
S extends `${Sub}${string}`
|
|
265
|
+
? Index['length']
|
|
266
|
+
: (
|
|
267
|
+
S extends `${string}${infer Rest}`
|
|
268
|
+
? InternalStringIndexOf<Rest, Sub, [...Index, unknown]>
|
|
269
|
+
: -1
|
|
270
|
+
)
|
|
271
|
+
/**
|
|
272
|
+
* Find the first occurrence index of a substring.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```
|
|
276
|
+
* // Expect: 2
|
|
277
|
+
* type Example1 = StringIndexOf<'hello', 'll'>
|
|
278
|
+
* // Expect: -1
|
|
279
|
+
* type Example2 = StringIndexOf<'hello', 'x'>
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export type StringIndexOf<S extends string, Sub extends string> =
|
|
283
|
+
Sub extends '' ? 0 : InternalStringIndexOf<S, Sub>
|
|
284
|
+
|
|
285
|
+
type InternalStringCommonPrefix<S1 extends string, S2 extends string, Acc extends string = ''> =
|
|
286
|
+
S1 extends `${infer First1}${infer Rest1}`
|
|
287
|
+
? (
|
|
288
|
+
S2 extends `${infer First2}${infer Rest2}`
|
|
289
|
+
? (
|
|
290
|
+
First1 extends First2
|
|
291
|
+
? InternalStringCommonPrefix<Rest1, Rest2, `${Acc}${First1}`>
|
|
292
|
+
: Acc
|
|
293
|
+
)
|
|
294
|
+
: Acc
|
|
295
|
+
)
|
|
296
|
+
: Acc
|
|
297
|
+
/**
|
|
298
|
+
* Get the common prefix of two strings.
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* ```
|
|
302
|
+
* // Expect: 'hello'
|
|
303
|
+
* type Example1 = StringCommonPrefix<'helloworld', 'hellothere'>
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
export type StringCommonPrefix<S1 extends string, S2 extends string> = InternalStringCommonPrefix<S1, S2>
|
|
307
|
+
|
|
308
|
+
// ============================================================================
|
|
309
|
+
// String Generation
|
|
310
|
+
// ============================================================================
|
|
311
|
+
|
|
312
|
+
type InternalRepeat<
|
|
313
|
+
S extends string,
|
|
314
|
+
N extends number,
|
|
315
|
+
Acc extends string,
|
|
316
|
+
Count extends readonly unknown[],
|
|
317
|
+
> = Count['length'] extends N ? Acc : InternalRepeat<S, N, `${Acc}${S}`, [...Count, unknown]>
|
|
318
|
+
/**
|
|
319
|
+
* Repeat a string N times.
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```
|
|
323
|
+
* // Expect: 'ababab'
|
|
324
|
+
* type Example1 = StringRepeat<'ab', 3>
|
|
325
|
+
* ```
|
|
326
|
+
*/
|
|
327
|
+
export type StringRepeat<
|
|
328
|
+
S extends string,
|
|
329
|
+
N extends number
|
|
330
|
+
> = InternalRepeat<S, N, '', []>
|
|
331
|
+
|
|
332
|
+
// ============================================================================
|
|
333
|
+
// Extraction
|
|
334
|
+
// ============================================================================
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Extract the first character of a string.
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```
|
|
341
|
+
* // Expect: 'h'
|
|
342
|
+
* type Example1 = StringFirst<'hello'>
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
export type StringFirst<S extends string> = S extends `${infer First}${string}` ? First : never
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Extract the last character of a string.
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* ```
|
|
352
|
+
* // Expect: 'o'
|
|
353
|
+
* type Example1 = StringLast<'hello'>
|
|
354
|
+
* ```
|
|
355
|
+
*/
|
|
356
|
+
export type StringLast<S extends string> = S extends `${infer Rest}${infer Last}` ? (Rest extends '' ? Last : StringLast<Rest>) : never
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Get the character at a specific index in a string.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```
|
|
363
|
+
* // Expect: 'e'
|
|
364
|
+
* type Example1 = StringAt<'hello', 1>
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
export type StringAt<S extends string, Index extends number> =
|
|
368
|
+
StringSplit<S, ''> extends (infer Characters extends string[])
|
|
369
|
+
? Characters[Index]
|
|
370
|
+
: never
|
|
371
|
+
|
|
372
|
+
// ============================================================================
|
|
373
|
+
// Manipulation
|
|
374
|
+
// ============================================================================
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Concatenate an array of strings.
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```
|
|
381
|
+
* // Expect: 'helloworld'
|
|
382
|
+
* type Example1 = StringConcat<['hello', 'world']>
|
|
383
|
+
* ```
|
|
384
|
+
*/
|
|
385
|
+
export type StringConcat<T extends readonly string[]> =
|
|
386
|
+
T extends [infer First extends string, ...infer Rest extends string[]]
|
|
387
|
+
? `${First}${StringConcat<Rest>}`
|
|
388
|
+
: ''
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Split a string into an array by a separator.
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* ```
|
|
395
|
+
* // Expect: ['a', 'b', 'c']
|
|
396
|
+
* type Example1 = StringSplit<'a,b,c', ','>
|
|
397
|
+
* ```
|
|
398
|
+
*/
|
|
399
|
+
export type StringSplit<S extends string, Separator extends string> = S extends `${infer First}${Separator}${infer Rest}`
|
|
400
|
+
? [First, ...StringSplit<Rest, Separator>]
|
|
401
|
+
: (S extends '' ? [] : [S])
|
|
402
|
+
|
|
403
|
+
type InternalStringSplitToWords<S extends string, Acc extends string[] = [], Current extends string = ''> =
|
|
404
|
+
S extends `${infer First}${infer Rest}`
|
|
405
|
+
? (
|
|
406
|
+
First extends ' ' | '-' | '_'
|
|
407
|
+
? (
|
|
408
|
+
Current extends ''
|
|
409
|
+
? InternalStringSplitToWords<Rest, Acc, ''>
|
|
410
|
+
: InternalStringSplitToWords<Rest, [...Acc, Current], ''>
|
|
411
|
+
)
|
|
412
|
+
: InternalStringSplitToWords<Rest, Acc, `${Current}${First}`>
|
|
413
|
+
)
|
|
414
|
+
: (Current extends '' ? Acc : [...Acc, Current])
|
|
415
|
+
/**
|
|
416
|
+
* Split a string into words (by spaces, hyphens, underscores).
|
|
417
|
+
*
|
|
418
|
+
* @example
|
|
419
|
+
* ```
|
|
420
|
+
* // Expect: ['hello', 'world', 'test']
|
|
421
|
+
* type Example1 = StringSplitToWords<'hello-world_test'>
|
|
422
|
+
* ```
|
|
423
|
+
*/
|
|
424
|
+
export type StringSplitToWords<S extends string> = InternalStringSplitToWords<S>
|
|
425
|
+
|
|
426
|
+
type InternalStringSplitToChunks<S extends string, Size extends number, Acc extends string[] = [], Current extends string = '', Counter extends readonly unknown[] = []> =
|
|
427
|
+
Counter['length'] extends Size
|
|
428
|
+
? InternalStringSplitToChunks<S, Size, [...Acc, Current], '', []>
|
|
429
|
+
: (
|
|
430
|
+
S extends `${infer First}${infer Rest}`
|
|
431
|
+
? InternalStringSplitToChunks<Rest, Size, Acc, `${Current}${First}`, [...Counter, unknown]>
|
|
432
|
+
: (Current extends '' ? Acc : [...Acc, Current])
|
|
433
|
+
)
|
|
434
|
+
/**
|
|
435
|
+
* Split a string into chunks of a specific size.
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* ```
|
|
439
|
+
* // Expect: ['he', 'll', 'o']
|
|
440
|
+
* type Example1 = StringSplitToChunks<'hello', 2>
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
export type StringSplitToChunks<S extends string, Size extends number> = InternalStringSplitToChunks<S, Size>
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Join an array of strings with a separator.
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```
|
|
450
|
+
* // Expect: 'a-b-c'
|
|
451
|
+
* type Example1 = StringJoin<['a', 'b', 'c'], '-'>
|
|
452
|
+
* ```
|
|
453
|
+
*/
|
|
454
|
+
export type StringJoin<T extends readonly string[], Separator extends string> =
|
|
455
|
+
T extends [infer First extends string, ...infer Rest extends string[]]
|
|
456
|
+
? (Rest extends [] ? First : `${First}${Separator}${StringJoin<Rest, Separator>}`)
|
|
457
|
+
: ''
|
|
458
|
+
|
|
459
|
+
type InternalReverse<S extends string, Acc extends string = ""> = S extends `${infer First}${infer Rest}`
|
|
460
|
+
? InternalReverse<Rest, `${First}${Acc}`>
|
|
461
|
+
: Acc
|
|
462
|
+
/**
|
|
463
|
+
* Reverse a string.
|
|
464
|
+
*
|
|
465
|
+
* @example
|
|
466
|
+
* ```
|
|
467
|
+
* // Expect: 'olleh'
|
|
468
|
+
* type Example1 = StringReverse<'hello'>
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
471
|
+
export type StringReverse<S extends string> = InternalReverse<S>
|
|
472
|
+
|
|
473
|
+
type InternalStringDropFirst<S extends string, N extends number, Counter extends readonly unknown[] = []> =
|
|
474
|
+
Counter['length'] extends N
|
|
475
|
+
? S
|
|
476
|
+
: (S extends `${string}${infer Rest}` ? InternalStringDropFirst<Rest, N, [...Counter, unknown]> : S)
|
|
477
|
+
/**
|
|
478
|
+
* Drop the first N characters from a string.
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* ```
|
|
482
|
+
* // Expect: 'lo'
|
|
483
|
+
* type Example1 = StringDropFirst<'hello', 3>
|
|
484
|
+
* ```
|
|
485
|
+
*/
|
|
486
|
+
export type StringDropFirst<S extends string, N extends number> = InternalStringDropFirst<S, N>
|
|
487
|
+
|
|
488
|
+
type InternalStringDropLast<S extends string, N extends number, Counter extends readonly unknown[] = []> =
|
|
489
|
+
Counter['length'] extends N
|
|
490
|
+
? S
|
|
491
|
+
: (S extends `${infer Rest}${string}` ? InternalStringDropLast<Rest, N, [...Counter, unknown]> : S)
|
|
492
|
+
/**
|
|
493
|
+
* Drop the last N characters from a string.
|
|
494
|
+
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```
|
|
497
|
+
* // Expect: 'hel'
|
|
498
|
+
* type Example1 = StringDropLast<'hello', 2>
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
export type StringDropLast<S extends string, N extends number> = InternalStringDropLast<S, N>
|
|
502
|
+
|
|
503
|
+
type InternalStringTakeFirst<S extends string, N extends number, Acc extends string = '', Counter extends readonly unknown[] = []> =
|
|
504
|
+
Counter['length'] extends N
|
|
505
|
+
? Acc
|
|
506
|
+
: (S extends `${infer First}${infer Rest}` ? InternalStringTakeFirst<Rest, N, `${Acc}${First}`, [...Counter, unknown]> : Acc)
|
|
507
|
+
/**
|
|
508
|
+
* Take the first N characters from a string.
|
|
509
|
+
*
|
|
510
|
+
* @example
|
|
511
|
+
* ```
|
|
512
|
+
* // Expect: 'hel'
|
|
513
|
+
* type Example1 = StringTakeFirst<'hello', 3>
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
export type StringTakeFirst<S extends string, N extends number> = InternalStringTakeFirst<S, N>
|
|
517
|
+
|
|
518
|
+
type InternalStringTakeLast<S extends string, N extends number, Counter extends readonly unknown[] = []> =
|
|
519
|
+
Counter['length'] extends N
|
|
520
|
+
? S
|
|
521
|
+
: (S extends `${string}${infer Rest}` ? InternalStringTakeLast<Rest, N, [...Counter, unknown]> : '')
|
|
522
|
+
/**
|
|
523
|
+
* Take the last N characters from a string.
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```
|
|
527
|
+
* // Expect: 'lo'
|
|
528
|
+
* type Example1 = StringTakeLast<'hello', 2>
|
|
529
|
+
* ```
|
|
530
|
+
*/
|
|
531
|
+
export type StringTakeLast<S extends string, N extends number> = InternalStringTakeLast<S, N>
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Replace the first occurrence of a substring with another.
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* ```
|
|
538
|
+
* // Expect: 'hello typescript world'
|
|
539
|
+
* type Example1 = StringReplaceFirst<'hello world world', 'world', 'typescript'>
|
|
540
|
+
* ```
|
|
541
|
+
*/
|
|
542
|
+
export type StringReplaceFirst<S extends string, From extends string, To extends string> =
|
|
543
|
+
From extends ''
|
|
544
|
+
? S
|
|
545
|
+
: (
|
|
546
|
+
S extends `${infer Start}${From}${infer Rest}`
|
|
547
|
+
? `${Start}${To}${Rest}`
|
|
548
|
+
: S
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Replace all occurrences of a substring with another.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```
|
|
556
|
+
* // Expect: 'hello typescript typescript'
|
|
557
|
+
* type Example1 = StringReplaceAll<'hello world world', 'world', 'typescript'>
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
export type StringReplaceAll<S extends string, From extends string, To extends string> =
|
|
561
|
+
From extends ''
|
|
562
|
+
? S
|
|
563
|
+
: (
|
|
564
|
+
S extends `${infer Start}${From}${infer Rest}`
|
|
565
|
+
? `${Start}${To}${StringReplaceAll<Rest, From, To>}`
|
|
566
|
+
: S
|
|
567
|
+
)
|
|
568
|
+
|
|
569
|
+
type InternalStringInsert<
|
|
570
|
+
S extends string,
|
|
571
|
+
Index extends number,
|
|
572
|
+
Insert extends string,
|
|
573
|
+
Counter extends readonly unknown[] = [],
|
|
574
|
+
Acc extends string = ''
|
|
575
|
+
> =
|
|
576
|
+
Counter['length'] extends Index
|
|
577
|
+
? `${Acc}${Insert}${S}`
|
|
578
|
+
: (
|
|
579
|
+
S extends `${infer First}${infer Rest}`
|
|
580
|
+
? InternalStringInsert<Rest, Index, Insert, [...Counter, unknown], `${Acc}${First}`>
|
|
581
|
+
: `${Acc}${Insert}`
|
|
582
|
+
)
|
|
583
|
+
/**
|
|
584
|
+
* Insert a string at a specific index.
|
|
585
|
+
*
|
|
586
|
+
* @example
|
|
587
|
+
* ```
|
|
588
|
+
* // Expect: 'helXYZlo'
|
|
589
|
+
* type Example1 = StringInsert<'hello', 3, 'XYZ'>
|
|
590
|
+
* ```
|
|
591
|
+
*/
|
|
592
|
+
export type StringInsert<S extends string, Index extends number, Insert extends string> =
|
|
593
|
+
InternalStringInsert<S, Index, Insert>
|
|
594
|
+
|
|
595
|
+
type InternalStringRemove<
|
|
596
|
+
S extends string,
|
|
597
|
+
Start extends number,
|
|
598
|
+
End extends number,
|
|
599
|
+
Counter extends readonly unknown[] = [],
|
|
600
|
+
Acc extends string = ''
|
|
601
|
+
> =
|
|
602
|
+
S extends `${infer First}${infer Rest}`
|
|
603
|
+
? (
|
|
604
|
+
Counter['length'] extends Start
|
|
605
|
+
? InternalStringRemoveRange<Rest, End, Start, [...Counter, unknown]>
|
|
606
|
+
: InternalStringRemove<Rest, Start, End, [...Counter, unknown], `${Acc}${First}`>
|
|
607
|
+
)
|
|
608
|
+
: Acc
|
|
609
|
+
type InternalStringRemoveRange<
|
|
610
|
+
S extends string,
|
|
611
|
+
End extends number,
|
|
612
|
+
Start extends number,
|
|
613
|
+
Counter extends readonly unknown[]
|
|
614
|
+
> =
|
|
615
|
+
Counter['length'] extends End
|
|
616
|
+
? S
|
|
617
|
+
: (
|
|
618
|
+
S extends `${string}${infer Rest}`
|
|
619
|
+
? InternalStringRemoveRange<Rest, End, Start, [...Counter, unknown]>
|
|
620
|
+
: ''
|
|
621
|
+
)
|
|
622
|
+
/**
|
|
623
|
+
* Remove characters from a string between start and end indices.
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```
|
|
627
|
+
* // Expect: 'heo'
|
|
628
|
+
* type Example1 = StringRemove<'hello', 2, 4>
|
|
629
|
+
* ```
|
|
630
|
+
*/
|
|
631
|
+
export type StringRemove<S extends string, Start extends number, End extends number> =
|
|
632
|
+
InternalStringRemove<S, Start, End>
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Trim whitespace from both ends of a string.
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* ```
|
|
639
|
+
* // Expect: 'hello'
|
|
640
|
+
* type Example1 = StringTrim<' hello '>
|
|
641
|
+
* ```
|
|
642
|
+
*/
|
|
643
|
+
export type StringTrim<S extends string> = S extends ` ${infer Rest}` | `\n${infer Rest}` | `\t${infer Rest}`
|
|
644
|
+
? StringTrim<Rest>
|
|
645
|
+
: (
|
|
646
|
+
S extends `${infer Rest} ` | `${infer Rest}\n` | `${infer Rest}\t`
|
|
647
|
+
? StringTrim<Rest>
|
|
648
|
+
: S
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Trim whitespace from the start of a string.
|
|
653
|
+
*
|
|
654
|
+
* @example
|
|
655
|
+
* ```
|
|
656
|
+
* // Expect: 'hello'
|
|
657
|
+
* type Example1 = StringTrimStart<' hello'>
|
|
658
|
+
* ```
|
|
659
|
+
*/
|
|
660
|
+
export type StringTrimStart<S extends string> = S extends ` ${infer Rest}` | `\n${infer Rest}` | `\t${infer Rest}`
|
|
661
|
+
? StringTrimStart<Rest>
|
|
662
|
+
: S
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Trim whitespace from the end of a string.
|
|
666
|
+
*
|
|
667
|
+
* @example
|
|
668
|
+
* ```
|
|
669
|
+
* // Expect: 'hello'
|
|
670
|
+
* type Example1 = StringTrimEnd<'hello '>
|
|
671
|
+
* ```
|
|
672
|
+
*/
|
|
673
|
+
export type StringTrimEnd<S extends string> = S extends `${infer Rest} ` | `${infer Rest}\n` | `${infer Rest}\t`
|
|
674
|
+
? StringTrimEnd<Rest>
|
|
675
|
+
: S
|
|
676
|
+
|
|
677
|
+
type InternalStringPadStart<
|
|
678
|
+
S extends string,
|
|
679
|
+
Length extends number,
|
|
680
|
+
Fill extends string,
|
|
681
|
+
Acc extends string,
|
|
682
|
+
Count extends readonly unknown[],
|
|
683
|
+
> = StringLength<Acc> extends Length ? Acc : InternalStringPadStart<S, Length, Fill, `${Fill}${Acc}`, [...Count, unknown]>
|
|
684
|
+
/**
|
|
685
|
+
* Pad a string on the left to a target length with a fill string.
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* ```
|
|
689
|
+
* // Expect: '005'
|
|
690
|
+
* type Example1 = StringPadStart<'5', 3, '0'>
|
|
691
|
+
* ```
|
|
692
|
+
*/
|
|
693
|
+
export type StringPadStart<
|
|
694
|
+
S extends string,
|
|
695
|
+
Length extends number,
|
|
696
|
+
Fill extends string = ' ',
|
|
697
|
+
> = InternalStringPadStart<S, Length, Fill, S, []>
|
|
698
|
+
|
|
699
|
+
type InternalStringPadEnd<
|
|
700
|
+
S extends string,
|
|
701
|
+
Length extends number,
|
|
702
|
+
Fill extends string,
|
|
703
|
+
Acc extends string,
|
|
704
|
+
Count extends readonly unknown[],
|
|
705
|
+
> = StringLength<Acc> extends Length ? Acc : InternalStringPadEnd<S, Length, Fill, `${Acc}${Fill}`, [...Count, unknown]>
|
|
706
|
+
/**
|
|
707
|
+
* Pad a string on the right to a target length with a fill string.
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* ```
|
|
711
|
+
* // Expect: '500'
|
|
712
|
+
* type Example1 = StringPadEnd<'5', 3, '0'>
|
|
713
|
+
* ```
|
|
714
|
+
*/
|
|
715
|
+
export type StringPadEnd<
|
|
716
|
+
S extends string,
|
|
717
|
+
Length extends number,
|
|
718
|
+
Fill extends string = ' ',
|
|
719
|
+
> = InternalStringPadEnd<S, Length, Fill, S, []>
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Convert a string to uppercase.
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* ```
|
|
726
|
+
* // Expect: 'HELLO'
|
|
727
|
+
* type Example1 = StringUppercase<'hello'>
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
export type StringUppercase<S extends string> = Uppercase<S>
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Convert a string to lowercase.
|
|
734
|
+
*
|
|
735
|
+
* @example
|
|
736
|
+
* ```
|
|
737
|
+
* // Expect: 'hello'
|
|
738
|
+
* type Example1 = StringLowercase<'HELLO'>
|
|
739
|
+
* ```
|
|
740
|
+
*/
|
|
741
|
+
export type StringLowercase<S extends string> = Lowercase<S>
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Capitalize the first letter of a string.
|
|
745
|
+
*
|
|
746
|
+
* @example
|
|
747
|
+
* ```
|
|
748
|
+
* // Expect: 'Hello'
|
|
749
|
+
* type Example1 = StringCapitalize<'hello'>
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
export type StringCapitalize<S extends string> = S extends `${infer First}${infer Rest}`
|
|
753
|
+
? `${Uppercase<First>}${Rest}`
|
|
754
|
+
: S
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Uncapitalize the first letter of a string.
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* ```
|
|
761
|
+
* // Expect: 'hello'
|
|
762
|
+
* type Example1 = StringUncapitalize<'Hello'>
|
|
763
|
+
* ```
|
|
764
|
+
*/
|
|
765
|
+
export type StringUncapitalize<S extends string> = S extends `${infer First}${infer Rest}`
|
|
766
|
+
? `${Lowercase<First>}${Rest}`
|
|
767
|
+
: S
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Convert a string to kebab-case.
|
|
771
|
+
*
|
|
772
|
+
* @example
|
|
773
|
+
* ```
|
|
774
|
+
* // Expect: 'hello-world'
|
|
775
|
+
* type Example1 = StringKebabCase<'helloWorld'>
|
|
776
|
+
* // Expect: 'hello-world'
|
|
777
|
+
* type Example2 = StringKebabCase<'HelloWorld'>
|
|
778
|
+
* // Expect: 'hello-world--test'
|
|
779
|
+
* type Example3 = StringKebabCase<'helloWorld-Test'>
|
|
780
|
+
* // Expect: 'u-r-l-string'
|
|
781
|
+
* type Example4 = StringKebabCase<'URLString'>
|
|
782
|
+
* ```
|
|
783
|
+
*/
|
|
784
|
+
export type StringKebabCase<
|
|
785
|
+
S extends string,
|
|
786
|
+
First extends boolean = true
|
|
787
|
+
> =
|
|
788
|
+
S extends `${infer C}${infer Rest}`
|
|
789
|
+
? (
|
|
790
|
+
C extends Lowercase<C>
|
|
791
|
+
? `${C}${StringKebabCase<Rest, false>}`
|
|
792
|
+
: (
|
|
793
|
+
First extends true
|
|
794
|
+
? `${Lowercase<C>}${StringKebabCase<Rest, false>}`
|
|
795
|
+
: `-${Lowercase<C>}${StringKebabCase<Rest, false>}`
|
|
796
|
+
)
|
|
797
|
+
)
|
|
798
|
+
: ''
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Convert a string to camelCase.
|
|
802
|
+
*
|
|
803
|
+
* @example
|
|
804
|
+
* ```
|
|
805
|
+
* // Expect: 'helloWorld'
|
|
806
|
+
* type Example1 = StringCamelCase<'hello-world'>
|
|
807
|
+
* // Expect: 'helloWorld'
|
|
808
|
+
* type Example2 = StringCamelCase<'hello--world'>
|
|
809
|
+
* ```
|
|
810
|
+
*/
|
|
811
|
+
export type StringCamelCase<S extends string> = S extends `${infer First}-${infer Rest}`
|
|
812
|
+
? `${Lowercase<First>}${StringCapitalize<StringCamelCase<Rest>>}`
|
|
813
|
+
: (
|
|
814
|
+
S extends `${infer First}_${infer Rest}`
|
|
815
|
+
? `${Lowercase<First>}${StringCapitalize<StringCamelCase<Rest>>}`
|
|
816
|
+
: Lowercase<S>
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
/**
|
|
820
|
+
* Convert a string to PascalCase.
|
|
821
|
+
*
|
|
822
|
+
* @example
|
|
823
|
+
* ```
|
|
824
|
+
* // Expect: 'HelloWorld'
|
|
825
|
+
* type Example1 = StringPascalCase<'hello-world'>
|
|
826
|
+
* // Expect: 'HelloWorld'
|
|
827
|
+
* type Example2 = StringPascalCase<'HELLO_WORLD'>
|
|
828
|
+
* ```
|
|
829
|
+
*/
|
|
830
|
+
export type StringPascalCase<S extends string> = StringCapitalize<StringCamelCase<S>>
|
|
831
|
+
|
|
832
|
+
/**
|
|
833
|
+
* Convert a string to snake_case.
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* ```
|
|
837
|
+
* // Expect: 'hello_world'
|
|
838
|
+
* type Example1 = StringSnakeCase<'helloWorld'>
|
|
839
|
+
* ```
|
|
840
|
+
*/
|
|
841
|
+
export type StringSnakeCase<
|
|
842
|
+
S extends string,
|
|
843
|
+
First extends boolean = true
|
|
844
|
+
> =
|
|
845
|
+
S extends `${infer C}${infer Rest}`
|
|
846
|
+
? (
|
|
847
|
+
C extends Lowercase<C>
|
|
848
|
+
? `${C}${StringSnakeCase<Rest, false>}`
|
|
849
|
+
: (
|
|
850
|
+
First extends true
|
|
851
|
+
? `${Lowercase<C>}${StringSnakeCase<Rest, false>}`
|
|
852
|
+
: `_${Lowercase<C>}${StringSnakeCase<Rest, false>}`
|
|
853
|
+
)
|
|
854
|
+
)
|
|
855
|
+
: ''
|
|
856
|
+
|
|
857
|
+
type InternalStringTitleCase<S extends string, ShouldCapitalize extends boolean = true> =
|
|
858
|
+
S extends `${infer First}${infer Rest}`
|
|
859
|
+
? (
|
|
860
|
+
First extends ' ' | '-' | '_'
|
|
861
|
+
? `${First}${InternalStringTitleCase<Rest, true>}`
|
|
862
|
+
: (
|
|
863
|
+
ShouldCapitalize extends true
|
|
864
|
+
? `${Uppercase<First>}${InternalStringTitleCase<Rest, false>}`
|
|
865
|
+
: `${Lowercase<First>}${InternalStringTitleCase<Rest, false>}`
|
|
866
|
+
)
|
|
867
|
+
)
|
|
868
|
+
: ''
|
|
869
|
+
/**
|
|
870
|
+
* Convert a string to Title Case (capitalize first letter of each word).
|
|
871
|
+
*
|
|
872
|
+
* @example
|
|
873
|
+
* ```
|
|
874
|
+
* // Expect: 'Hello World'
|
|
875
|
+
* type Example1 = StringTitleCase<'hello world'>
|
|
876
|
+
* // Expect: 'Hello-World-Test'
|
|
877
|
+
* type Example2 = StringTitleCase<'hello-world-test'>
|
|
878
|
+
* ```
|
|
879
|
+
*/
|
|
880
|
+
export type StringTitleCase<S extends string> = InternalStringTitleCase<S>
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Convert a string to CONSTANT_CASE.
|
|
884
|
+
*
|
|
885
|
+
* @example
|
|
886
|
+
* ```
|
|
887
|
+
* // Expect: 'HELLO_WORLD'
|
|
888
|
+
* type Example1 = StringConstantCase<'helloWorld'>
|
|
889
|
+
* // Expect: 'HELLO_WORLD'
|
|
890
|
+
* type Example2 = StringConstantCase<'hello-world'>
|
|
891
|
+
* ```
|
|
892
|
+
*/
|
|
893
|
+
export type StringConstantCase<S extends string, First extends boolean = true> =
|
|
894
|
+
S extends `${infer C}${infer Rest}`
|
|
895
|
+
? (
|
|
896
|
+
C extends '-' | '_' | ' '
|
|
897
|
+
? `_${StringConstantCase<Rest, true>}`
|
|
898
|
+
: (
|
|
899
|
+
C extends Lowercase<C>
|
|
900
|
+
? `${Uppercase<C>}${StringConstantCase<Rest, false>}`
|
|
901
|
+
: (
|
|
902
|
+
First extends true
|
|
903
|
+
? `${Uppercase<C>}${StringConstantCase<Rest, false>}`
|
|
904
|
+
: `_${Uppercase<C>}${StringConstantCase<Rest, false>}`
|
|
905
|
+
)
|
|
906
|
+
)
|
|
907
|
+
)
|
|
908
|
+
: ''
|
|
909
|
+
|
|
910
|
+
type InternalStringSwapCase<S extends string> =
|
|
911
|
+
S extends `${infer First}${infer Rest}`
|
|
912
|
+
? (
|
|
913
|
+
First extends Uppercase<First>
|
|
914
|
+
? `${Lowercase<First>}${InternalStringSwapCase<Rest>}`
|
|
915
|
+
: `${Uppercase<First>}${InternalStringSwapCase<Rest>}`
|
|
916
|
+
)
|
|
917
|
+
: ''
|
|
918
|
+
/**
|
|
919
|
+
* Swap the case of each character in a string.
|
|
920
|
+
*
|
|
921
|
+
* @example
|
|
922
|
+
* ```
|
|
923
|
+
* // Expect: 'HELLO world'
|
|
924
|
+
* type Example1 = StringSwapCase<'hello WORLD'>
|
|
925
|
+
* ```
|
|
926
|
+
*/
|
|
927
|
+
export type StringSwapCase<S extends string> = InternalStringSwapCase<S>
|
|
928
|
+
|
|
929
|
+
type InternalStringEllipsis<S extends string, Max extends number, Counter extends readonly unknown[] = [], Acc extends string = ''> =
|
|
930
|
+
Counter['length'] extends Max
|
|
931
|
+
? `${Acc}...`
|
|
932
|
+
: (
|
|
933
|
+
S extends `${infer First}${infer Rest}`
|
|
934
|
+
? InternalStringEllipsis<Rest, Max, [...Counter, unknown], `${Acc}${First}`>
|
|
935
|
+
: Acc
|
|
936
|
+
)
|
|
937
|
+
/**
|
|
938
|
+
* Truncate a string with ellipsis if it exceeds maximum length.
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* ```
|
|
942
|
+
* // Expect: 'hel...'
|
|
943
|
+
* type Example1 = StringEllipsis<'hello world', 3>
|
|
944
|
+
* ```
|
|
945
|
+
*/
|
|
946
|
+
export type StringEllipsis<S extends string, Max extends number> = InternalStringEllipsis<S, Max>
|
|
947
|
+
|
|
948
|
+
type InternalStringIndent<S extends string, Spaces extends string> =
|
|
949
|
+
S extends `${infer Line}\n${infer Rest}`
|
|
950
|
+
? `${Spaces}${Line}\n${InternalStringIndent<Rest, Spaces>}`
|
|
951
|
+
: `${Spaces}${S}`
|
|
952
|
+
/**
|
|
953
|
+
* Add indentation to each line of a string.
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* ```
|
|
957
|
+
* // Expect: ' hello\n world'
|
|
958
|
+
* type Example1 = StringIndent<'hello\nworld', ' '>
|
|
959
|
+
* ```
|
|
960
|
+
*/
|
|
961
|
+
export type StringIndent<S extends string, Spaces extends string = ' '> = InternalStringIndent<S, Spaces>
|
|
962
|
+
|
|
963
|
+
// ============================================================================
|
|
964
|
+
// Conversion
|
|
965
|
+
// ============================================================================
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Convert a string literal to a number type (limited to small numbers).
|
|
969
|
+
*
|
|
970
|
+
* @example
|
|
971
|
+
* ```
|
|
972
|
+
* // Expect: number
|
|
973
|
+
* type Example1 = StringToNumber<'123'>
|
|
974
|
+
* ```
|
|
975
|
+
*/
|
|
976
|
+
export type StringToNumber<S extends string> =
|
|
977
|
+
S extends `${infer N extends number}` ? N : never
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Convert string 'true' or 'false' to boolean type.
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* ```
|
|
984
|
+
* // Expect: true
|
|
985
|
+
* type Example1 = StringToBoolean<'true'>
|
|
986
|
+
* // Expect: false
|
|
987
|
+
* type Example2 = StringToBoolean<'false'>
|
|
988
|
+
* ```
|
|
989
|
+
*/
|
|
990
|
+
export type StringToBoolean<S extends string> =
|
|
991
|
+
S extends 'true' ? true :
|
|
992
|
+
S extends 'false' ? false :
|
|
993
|
+
never
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* Convert a string to an array of characters.
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* ```
|
|
1000
|
+
* // Expect: ['h', 'e', 'l', 'l', 'o']
|
|
1001
|
+
* type Example1 = StringToTuple<'hello'>
|
|
1002
|
+
* ```
|
|
1003
|
+
*/
|
|
1004
|
+
export type StringToTuple<S extends string> = StringSplit<S, ''>
|