@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.
Files changed (91) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +30 -1
  3. package/dist/index.js +4 -2
  4. package/dist/index.js.map +22 -4
  5. package/package.json +3 -3
  6. package/scripts/build.ts +4 -4
  7. package/src/basic/README.md +144 -0
  8. package/src/basic/array.ts +872 -0
  9. package/src/basic/bigint.ts +114 -0
  10. package/src/basic/boolean.ts +180 -0
  11. package/src/basic/enhance.ts +10 -0
  12. package/src/basic/error.ts +51 -0
  13. package/src/basic/function.ts +453 -0
  14. package/src/basic/helper.ts +276 -0
  15. package/src/basic/index.ts +17 -0
  16. package/src/basic/is.ts +320 -0
  17. package/src/basic/number.ts +178 -0
  18. package/src/basic/object.ts +140 -0
  19. package/src/basic/promise.ts +464 -0
  20. package/src/basic/regexp.ts +7 -0
  21. package/src/basic/stream.ts +140 -0
  22. package/src/basic/string.ts +308 -0
  23. package/src/basic/symbol.ts +164 -0
  24. package/src/basic/temporal.ts +224 -0
  25. package/src/encoding/README.md +105 -0
  26. package/src/encoding/base64.ts +98 -0
  27. package/src/encoding/index.ts +1 -0
  28. package/src/index.ts +4 -0
  29. package/src/random/README.md +109 -0
  30. package/src/random/index.ts +1 -0
  31. package/src/random/uuid.ts +103 -0
  32. package/src/type/README.md +330 -0
  33. package/src/type/array.ts +5 -0
  34. package/src/type/boolean.ts +471 -0
  35. package/src/type/class.ts +419 -0
  36. package/src/type/function.ts +1519 -0
  37. package/src/type/helper.ts +135 -0
  38. package/src/type/index.ts +14 -0
  39. package/src/type/intersection.ts +93 -0
  40. package/src/type/is.ts +247 -0
  41. package/src/type/iteration.ts +233 -0
  42. package/src/type/number.ts +732 -0
  43. package/src/type/object.ts +788 -0
  44. package/src/type/path.ts +73 -0
  45. package/src/type/string.ts +1004 -0
  46. package/src/type/tuple.ts +2424 -0
  47. package/src/type/union.ts +108 -0
  48. package/tests/unit/basic/array.spec.ts +290 -0
  49. package/tests/unit/basic/bigint.spec.ts +50 -0
  50. package/tests/unit/basic/boolean.spec.ts +74 -0
  51. package/tests/unit/basic/error.spec.ts +32 -0
  52. package/tests/unit/basic/function.spec.ts +175 -0
  53. package/tests/unit/basic/helper.spec.ts +118 -0
  54. package/tests/unit/basic/number.spec.ts +74 -0
  55. package/tests/unit/basic/object.spec.ts +46 -0
  56. package/tests/unit/basic/promise.spec.ts +232 -0
  57. package/tests/unit/basic/regexp.spec.ts +11 -0
  58. package/tests/unit/basic/stream.spec.ts +120 -0
  59. package/tests/unit/basic/string.spec.ts +74 -0
  60. package/tests/unit/basic/symbol.spec.ts +72 -0
  61. package/tests/unit/basic/temporal.spec.ts +78 -0
  62. package/tests/unit/encoding/base64.spec.ts +40 -0
  63. package/tests/unit/random/uuid.spec.ts +37 -0
  64. package/dist/index.d.ts +0 -2
  65. package/dist/index.d.ts.map +0 -1
  66. package/dist/reactor/index.d.ts +0 -3
  67. package/dist/reactor/index.d.ts.map +0 -1
  68. package/dist/reactor/reactor-core/flags.d.ts +0 -99
  69. package/dist/reactor/reactor-core/flags.d.ts.map +0 -1
  70. package/dist/reactor/reactor-core/index.d.ts +0 -4
  71. package/dist/reactor/reactor-core/index.d.ts.map +0 -1
  72. package/dist/reactor/reactor-core/primitive.d.ts +0 -276
  73. package/dist/reactor/reactor-core/primitive.d.ts.map +0 -1
  74. package/dist/reactor/reactor-core/reactive-system.d.ts +0 -241
  75. package/dist/reactor/reactor-core/reactive-system.d.ts.map +0 -1
  76. package/dist/reactor/reactor-operators/branch.d.ts +0 -19
  77. package/dist/reactor/reactor-operators/branch.d.ts.map +0 -1
  78. package/dist/reactor/reactor-operators/convert.d.ts +0 -30
  79. package/dist/reactor/reactor-operators/convert.d.ts.map +0 -1
  80. package/dist/reactor/reactor-operators/create.d.ts +0 -26
  81. package/dist/reactor/reactor-operators/create.d.ts.map +0 -1
  82. package/dist/reactor/reactor-operators/filter.d.ts +0 -269
  83. package/dist/reactor/reactor-operators/filter.d.ts.map +0 -1
  84. package/dist/reactor/reactor-operators/index.d.ts +0 -8
  85. package/dist/reactor/reactor-operators/index.d.ts.map +0 -1
  86. package/dist/reactor/reactor-operators/join.d.ts +0 -48
  87. package/dist/reactor/reactor-operators/join.d.ts.map +0 -1
  88. package/dist/reactor/reactor-operators/map.d.ts +0 -165
  89. package/dist/reactor/reactor-operators/map.d.ts.map +0 -1
  90. package/dist/reactor/reactor-operators/utility.d.ts +0 -48
  91. 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, ''>