ts-data-forge 13.0.0 → 14.0.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/README.md +1 -1
- package/dist/array/impl/array-utils-creation.d.mts +2 -2
- package/dist/array/impl/array-utils-creation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-creation.mjs.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts +56 -29
- package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-cast.mjs +23 -15
- package/dist/array/impl/array-utils-length-bounded-array-cast.mjs.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-guard.d.mts +126 -28
- package/dist/array/impl/array-utils-length-bounded-array-guard.d.mts.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-array-guard.mjs +39 -5
- package/dist/array/impl/array-utils-length-bounded-array-guard.mjs.map +1 -1
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts +167 -0
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts.map +1 -0
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs +87 -0
- package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs.map +1 -0
- package/dist/array/impl/array-utils-length-bounded-tuple-guard.d.mts +135 -0
- package/dist/array/impl/array-utils-length-bounded-tuple-guard.d.mts.map +1 -0
- package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs +41 -0
- package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs.map +1 -0
- package/dist/array/impl/array-utils-modification.d.mts +32 -18
- package/dist/array/impl/array-utils-modification.d.mts.map +1 -1
- package/dist/array/impl/array-utils-modification.mjs.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.d.mts +3 -3
- package/dist/array/impl/array-utils-reducing-value.d.mts.map +1 -1
- package/dist/array/impl/array-utils-reducing-value.mjs +1 -1
- package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
- package/dist/array/impl/array-utils-slicing.d.mts +11 -11
- package/dist/array/impl/array-utils-slicing.d.mts.map +1 -1
- package/dist/array/impl/array-utils-slicing.mjs +1 -1
- package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
- package/dist/array/impl/array-utils-transformation.d.mts +35 -23
- package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-transformation.mjs +2 -2
- package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
- package/dist/array/impl/array-utils-validation.d.mts +2 -124
- package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
- package/dist/array/impl/array-utils-validation.mjs +1 -7
- package/dist/array/impl/array-utils-validation.mjs.map +1 -1
- package/dist/array/impl/index.d.mts +2 -0
- package/dist/array/impl/index.d.mts.map +1 -1
- package/dist/array/impl/index.mjs +5 -3
- package/dist/array/impl/index.mjs.map +1 -1
- package/dist/object/object.mjs +1 -1
- package/dist/string/str.d.mts +44 -44
- package/dist/string/str.d.mts.map +1 -1
- package/dist/string/str.mjs +17 -17
- package/dist/string/str.mjs.map +1 -1
- package/package.json +2 -2
- package/src/array/impl/array-utils-creation.mts +7 -3
- package/src/array/impl/array-utils-length-bounded-array-cast.mts +88 -50
- package/src/array/impl/array-utils-length-bounded-array-cast.test.mts +40 -13
- package/src/array/impl/array-utils-length-bounded-array-guard.mts +248 -39
- package/src/array/impl/array-utils-length-bounded-array-guard.test.mts +135 -24
- package/src/array/impl/array-utils-length-bounded-tuple-cast.mts +364 -0
- package/src/array/impl/array-utils-length-bounded-tuple-cast.test.mts +207 -0
- package/src/array/impl/array-utils-length-bounded-tuple-guard.mts +266 -0
- package/src/array/impl/array-utils-length-bounded-tuple-guard.test.mts +441 -0
- package/src/array/impl/array-utils-length-constraint-preservation.test.mts +287 -0
- package/src/array/impl/array-utils-modification.mts +72 -57
- package/src/array/impl/array-utils-modification.test.mts +93 -6
- package/src/array/impl/array-utils-reducing-value.mts +5 -5
- package/src/array/impl/array-utils-slicing.mts +65 -43
- package/src/array/impl/array-utils-slicing.test.mts +55 -0
- package/src/array/impl/array-utils-transformation.mts +66 -39
- package/src/array/impl/array-utils-transformation.test.mts +62 -16
- package/src/array/impl/array-utils-validation.mts +2 -162
- package/src/array/impl/array-utils-validation.test.mts +0 -371
- package/src/array/impl/index.mts +2 -0
- package/src/collections/queue.test.mts +1 -1
- package/src/string/str.mts +69 -69
- package/src/string/str.test.mts +40 -40
package/src/string/str.mts
CHANGED
|
@@ -33,30 +33,30 @@ export namespace Str {
|
|
|
33
33
|
* ```ts
|
|
34
34
|
* const input: string = 'very-secret-password';
|
|
35
35
|
*
|
|
36
|
-
* assert.isTrue(Str.isMinLengthString(
|
|
36
|
+
* assert.isTrue(Str.isMinLengthString(12, input));
|
|
37
37
|
*
|
|
38
|
-
* assert.isFalse(Str.isMinLengthString('short'
|
|
38
|
+
* assert.isFalse(Str.isMinLengthString(12, 'short'));
|
|
39
39
|
*
|
|
40
|
-
* if (Str.isMinLengthString(
|
|
40
|
+
* if (Str.isMinLengthString(12, input)) {
|
|
41
41
|
* const nonEmpty: NonEmptyString = input; // OK (12 >= 1)
|
|
42
42
|
*
|
|
43
43
|
* assert.isTrue(nonEmpty.length >= 12);
|
|
44
44
|
* }
|
|
45
45
|
* ```
|
|
46
46
|
*
|
|
47
|
-
* @template S - The input string type (literal types are preserved).
|
|
48
47
|
* @template MinLength - The minimum number of characters (inclusive).
|
|
49
|
-
* @
|
|
48
|
+
* @template S - The input string type (literal types are preserved).
|
|
50
49
|
* @param minLength - The minimum number of characters (inclusive).
|
|
50
|
+
* @param s - The string to check.
|
|
51
51
|
* @returns `true` if `s.length >= minLength`, `false` otherwise. When `true`,
|
|
52
52
|
* TypeScript narrows `s` to `MinLengthString<MinLength> & S`.
|
|
53
53
|
*/
|
|
54
54
|
export const isMinLengthString = <
|
|
55
|
-
S extends string,
|
|
56
55
|
MinLength extends SupportedLength,
|
|
56
|
+
S extends string,
|
|
57
57
|
>(
|
|
58
|
-
s: S,
|
|
59
58
|
minLength: MinLength,
|
|
59
|
+
s: S,
|
|
60
60
|
): s is MinLengthString<MinLength> & S => s.length >= minLength;
|
|
61
61
|
|
|
62
62
|
/**
|
|
@@ -75,30 +75,30 @@ export namespace Str {
|
|
|
75
75
|
* ```ts
|
|
76
76
|
* const input: string = 'noshiro';
|
|
77
77
|
*
|
|
78
|
-
* assert.isTrue(Str.isMaxLengthString(
|
|
78
|
+
* assert.isTrue(Str.isMaxLengthString(32, input));
|
|
79
79
|
*
|
|
80
|
-
* assert.isFalse(Str.isMaxLengthString(
|
|
80
|
+
* assert.isFalse(Str.isMaxLengthString(3, input));
|
|
81
81
|
*
|
|
82
|
-
* if (Str.isMaxLengthString(
|
|
82
|
+
* if (Str.isMaxLengthString(32, input)) {
|
|
83
83
|
* const relaxed: MaxLengthString<64> = input; // OK (32 <= 64)
|
|
84
84
|
*
|
|
85
85
|
* assert.isTrue(relaxed.length <= 32);
|
|
86
86
|
* }
|
|
87
87
|
* ```
|
|
88
88
|
*
|
|
89
|
-
* @template S - The input string type (literal types are preserved).
|
|
90
89
|
* @template MaxLength - The maximum number of characters (inclusive).
|
|
91
|
-
* @
|
|
90
|
+
* @template S - The input string type (literal types are preserved).
|
|
92
91
|
* @param maxLength - The maximum number of characters (inclusive).
|
|
92
|
+
* @param s - The string to check.
|
|
93
93
|
* @returns `true` if `s.length <= maxLength`, `false` otherwise. When `true`,
|
|
94
94
|
* TypeScript narrows `s` to `MaxLengthString<MaxLength> & S`.
|
|
95
95
|
*/
|
|
96
96
|
export const isMaxLengthString = <
|
|
97
|
-
S extends string,
|
|
98
97
|
MaxLength extends SupportedLength,
|
|
98
|
+
S extends string,
|
|
99
99
|
>(
|
|
100
|
-
s: S,
|
|
101
100
|
maxLength: MaxLength,
|
|
101
|
+
s: S,
|
|
102
102
|
): s is MaxLengthString<MaxLength> & S => s.length <= maxLength;
|
|
103
103
|
|
|
104
104
|
/**
|
|
@@ -118,35 +118,35 @@ export namespace Str {
|
|
|
118
118
|
* ```ts
|
|
119
119
|
* const input: string = 'user-12345678';
|
|
120
120
|
*
|
|
121
|
-
* assert.isTrue(Str.isBoundedLengthString(
|
|
121
|
+
* assert.isTrue(Str.isBoundedLengthString(8, 16, input));
|
|
122
122
|
*
|
|
123
|
-
* assert.isFalse(Str.isBoundedLengthString(
|
|
123
|
+
* assert.isFalse(Str.isBoundedLengthString(8, 16, 'user'));
|
|
124
124
|
*
|
|
125
|
-
* if (Str.isBoundedLengthString(
|
|
125
|
+
* if (Str.isBoundedLengthString(8, 16, input)) {
|
|
126
126
|
* const userId: BoundedLengthString<1, 255> = input; // OK ([8, 16] ⊆ [1, 255])
|
|
127
127
|
*
|
|
128
128
|
* assert.isTrue(userId.length >= 8 && userId.length <= 16);
|
|
129
129
|
* }
|
|
130
130
|
* ```
|
|
131
131
|
*
|
|
132
|
-
* @template S - The input string type (literal types are preserved).
|
|
133
132
|
* @template MinLength - The minimum number of characters (inclusive).
|
|
134
133
|
* @template MaxLength - The maximum number of characters (inclusive).
|
|
135
|
-
* @
|
|
134
|
+
* @template S - The input string type (literal types are preserved).
|
|
136
135
|
* @param minLength - The minimum number of characters (inclusive).
|
|
137
136
|
* @param maxLength - The maximum number of characters (inclusive).
|
|
137
|
+
* @param s - The string to check.
|
|
138
138
|
* @returns `true` if `minLength <= s.length && s.length <= maxLength`, `false`
|
|
139
139
|
* otherwise. When `true`, TypeScript narrows `s` to
|
|
140
140
|
* `BoundedLengthString<MinLength, MaxLength> & S`.
|
|
141
141
|
*/
|
|
142
142
|
export const isBoundedLengthString = <
|
|
143
|
-
S extends string,
|
|
144
143
|
MinLength extends SupportedLength,
|
|
145
144
|
MaxLength extends SupportedLength,
|
|
145
|
+
S extends string,
|
|
146
146
|
>(
|
|
147
|
-
s: S,
|
|
148
147
|
minLength: MinLength,
|
|
149
148
|
maxLength: MaxLength,
|
|
149
|
+
s: S,
|
|
150
150
|
): s is BoundedLengthString<MinLength, MaxLength> & S =>
|
|
151
151
|
s.length >= minLength && s.length <= maxLength;
|
|
152
152
|
|
|
@@ -167,30 +167,30 @@ export namespace Str {
|
|
|
167
167
|
* ```ts
|
|
168
168
|
* const input: string = 'JP';
|
|
169
169
|
*
|
|
170
|
-
* assert.isTrue(Str.isFixedLengthString(
|
|
170
|
+
* assert.isTrue(Str.isFixedLengthString(2, input));
|
|
171
171
|
*
|
|
172
|
-
* assert.isFalse(Str.isFixedLengthString(
|
|
172
|
+
* assert.isFalse(Str.isFixedLengthString(3, input));
|
|
173
173
|
*
|
|
174
|
-
* if (Str.isFixedLengthString(
|
|
174
|
+
* if (Str.isFixedLengthString(2, input)) {
|
|
175
175
|
* const atMost5: MaxLengthString<5> = input; // OK (2 <= 5)
|
|
176
176
|
*
|
|
177
177
|
* assert.isTrue(atMost5.length === 2);
|
|
178
178
|
* }
|
|
179
179
|
* ```
|
|
180
180
|
*
|
|
181
|
-
* @template S - The input string type (literal types are preserved).
|
|
182
181
|
* @template Length - The exact number of characters.
|
|
183
|
-
* @
|
|
182
|
+
* @template S - The input string type (literal types are preserved).
|
|
184
183
|
* @param length - The exact number of characters.
|
|
184
|
+
* @param s - The string to check.
|
|
185
185
|
* @returns `true` if `s.length === length`, `false` otherwise. When `true`,
|
|
186
186
|
* TypeScript narrows `s` to `FixedLengthString<Length> & S`.
|
|
187
187
|
*/
|
|
188
188
|
export const isFixedLengthString = <
|
|
189
|
-
S extends string,
|
|
190
189
|
Length extends SupportedLength,
|
|
190
|
+
S extends string,
|
|
191
191
|
>(
|
|
192
|
-
s: S,
|
|
193
192
|
length: Length,
|
|
193
|
+
s: S,
|
|
194
194
|
): s is FixedLengthString<Length> & S => s.length === length;
|
|
195
195
|
|
|
196
196
|
/**
|
|
@@ -207,7 +207,7 @@ export namespace Str {
|
|
|
207
207
|
* @example
|
|
208
208
|
*
|
|
209
209
|
* ```ts
|
|
210
|
-
* const password = Str.asMinLengthString('very-secret-password'
|
|
210
|
+
* const password = Str.asMinLengthString(12, 'very-secret-password');
|
|
211
211
|
*
|
|
212
212
|
* const nonEmpty: NonEmptyString = password; // OK (>= 1)
|
|
213
213
|
*
|
|
@@ -218,20 +218,20 @@ export namespace Str {
|
|
|
218
218
|
*
|
|
219
219
|
* assert.strictEqual(asPassword('another-secret'), 'another-secret');
|
|
220
220
|
*
|
|
221
|
-
* assert.throws(() => Str.asMinLengthString('short'
|
|
221
|
+
* assert.throws(() => Str.asMinLengthString(12, 'short')); // length 5 < 12
|
|
222
222
|
* ```
|
|
223
223
|
*
|
|
224
|
-
* @template S - The input string type (literal types are preserved).
|
|
225
224
|
* @template MinLength - The minimum number of characters (inclusive).
|
|
226
|
-
* @
|
|
225
|
+
* @template S - The input string type (literal types are preserved).
|
|
227
226
|
* @param minLength - The minimum number of characters (inclusive).
|
|
227
|
+
* @param s - The string to cast.
|
|
228
228
|
* @returns `s` typed as `MinLengthString<MinLength> & S`.
|
|
229
229
|
* @throws {TypeError} If `s.length < minLength`.
|
|
230
230
|
*/
|
|
231
231
|
export function asMinLengthString<
|
|
232
|
-
S extends string,
|
|
233
232
|
MinLength extends SupportedLength,
|
|
234
|
-
|
|
233
|
+
S extends string,
|
|
234
|
+
>(minLength: MinLength, s: S): MinLengthString<MinLength> & S;
|
|
235
235
|
|
|
236
236
|
// Curried version
|
|
237
237
|
export function asMinLengthString<MinLength extends SupportedLength>(
|
|
@@ -240,7 +240,7 @@ export namespace Str {
|
|
|
240
240
|
|
|
241
241
|
export function asMinLengthString(
|
|
242
242
|
...args:
|
|
243
|
-
| readonly [
|
|
243
|
+
| readonly [minLength: SupportedLength, s: string]
|
|
244
244
|
| readonly [minLength: SupportedLength]
|
|
245
245
|
): string | ((s: string) => string) {
|
|
246
246
|
switch (args.length) {
|
|
@@ -248,17 +248,17 @@ export namespace Str {
|
|
|
248
248
|
return asMinLengthStringImpl(...args);
|
|
249
249
|
|
|
250
250
|
case 1:
|
|
251
|
-
return (s) => asMinLengthStringImpl(
|
|
251
|
+
return (s) => asMinLengthStringImpl(args[0], s);
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
255
|
const asMinLengthStringImpl = (
|
|
256
|
-
s: string,
|
|
257
256
|
minLength: SupportedLength,
|
|
257
|
+
s: string,
|
|
258
258
|
): string => {
|
|
259
259
|
const actualLength = s.length;
|
|
260
260
|
|
|
261
|
-
if (!isMinLengthString(
|
|
261
|
+
if (!isMinLengthString(minLength, s)) {
|
|
262
262
|
throw new TypeError(
|
|
263
263
|
`Expected a string of length >= ${minLength}, got a string of length ${actualLength}`,
|
|
264
264
|
);
|
|
@@ -281,7 +281,7 @@ export namespace Str {
|
|
|
281
281
|
* @example
|
|
282
282
|
*
|
|
283
283
|
* ```ts
|
|
284
|
-
* const userName = Str.asMaxLengthString('noshiro'
|
|
284
|
+
* const userName = Str.asMaxLengthString(32, 'noshiro');
|
|
285
285
|
*
|
|
286
286
|
* const relaxed: MaxLengthString<64> = userName; // OK (32 <= 64)
|
|
287
287
|
*
|
|
@@ -292,20 +292,20 @@ export namespace Str {
|
|
|
292
292
|
*
|
|
293
293
|
* assert.strictEqual(asUserName('another-user'), 'another-user');
|
|
294
294
|
*
|
|
295
|
-
* assert.throws(() => Str.asMaxLengthString('noshiro'
|
|
295
|
+
* assert.throws(() => Str.asMaxLengthString(3, 'noshiro')); // length 7 > 3
|
|
296
296
|
* ```
|
|
297
297
|
*
|
|
298
|
-
* @template S - The input string type (literal types are preserved).
|
|
299
298
|
* @template MaxLength - The maximum number of characters (inclusive).
|
|
300
|
-
* @
|
|
299
|
+
* @template S - The input string type (literal types are preserved).
|
|
301
300
|
* @param maxLength - The maximum number of characters (inclusive).
|
|
301
|
+
* @param s - The string to cast.
|
|
302
302
|
* @returns `s` typed as `MaxLengthString<MaxLength> & S`.
|
|
303
303
|
* @throws {TypeError} If `s.length > maxLength`.
|
|
304
304
|
*/
|
|
305
305
|
export function asMaxLengthString<
|
|
306
|
-
S extends string,
|
|
307
306
|
MaxLength extends SupportedLength,
|
|
308
|
-
|
|
307
|
+
S extends string,
|
|
308
|
+
>(maxLength: MaxLength, s: S): MaxLengthString<MaxLength> & S;
|
|
309
309
|
|
|
310
310
|
// Curried version
|
|
311
311
|
export function asMaxLengthString<MaxLength extends SupportedLength>(
|
|
@@ -314,7 +314,7 @@ export namespace Str {
|
|
|
314
314
|
|
|
315
315
|
export function asMaxLengthString(
|
|
316
316
|
...args:
|
|
317
|
-
| readonly [
|
|
317
|
+
| readonly [maxLength: SupportedLength, s: string]
|
|
318
318
|
| readonly [maxLength: SupportedLength]
|
|
319
319
|
): string | ((s: string) => string) {
|
|
320
320
|
switch (args.length) {
|
|
@@ -322,17 +322,17 @@ export namespace Str {
|
|
|
322
322
|
return asMaxLengthStringImpl(...args);
|
|
323
323
|
|
|
324
324
|
case 1:
|
|
325
|
-
return (s) => asMaxLengthStringImpl(
|
|
325
|
+
return (s) => asMaxLengthStringImpl(args[0], s);
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
|
|
329
329
|
const asMaxLengthStringImpl = (
|
|
330
|
-
s: string,
|
|
331
330
|
maxLength: SupportedLength,
|
|
331
|
+
s: string,
|
|
332
332
|
): string => {
|
|
333
333
|
const actualLength = s.length;
|
|
334
334
|
|
|
335
|
-
if (!isMaxLengthString(
|
|
335
|
+
if (!isMaxLengthString(maxLength, s)) {
|
|
336
336
|
throw new TypeError(
|
|
337
337
|
`Expected a string of length <= ${maxLength}, got a string of length ${actualLength}`,
|
|
338
338
|
);
|
|
@@ -357,7 +357,7 @@ export namespace Str {
|
|
|
357
357
|
* @example
|
|
358
358
|
*
|
|
359
359
|
* ```ts
|
|
360
|
-
* const userId = Str.asBoundedLengthString('user-12345678'
|
|
360
|
+
* const userId = Str.asBoundedLengthString(8, 16, 'user-12345678');
|
|
361
361
|
*
|
|
362
362
|
* const relaxed: BoundedLengthString<1, 255> = userId; // OK ([8, 16] ⊆ [1, 255])
|
|
363
363
|
*
|
|
@@ -368,26 +368,26 @@ export namespace Str {
|
|
|
368
368
|
*
|
|
369
369
|
* assert.strictEqual(asUserId('user-87654321'), 'user-87654321');
|
|
370
370
|
*
|
|
371
|
-
* assert.throws(() => Str.asBoundedLengthString(
|
|
371
|
+
* assert.throws(() => Str.asBoundedLengthString(8, 16, 'user')); // length 4 < 8
|
|
372
372
|
* ```
|
|
373
373
|
*
|
|
374
|
-
* @template S - The input string type (literal types are preserved).
|
|
375
374
|
* @template MinLength - The minimum number of characters (inclusive).
|
|
376
375
|
* @template MaxLength - The maximum number of characters (inclusive).
|
|
377
|
-
* @
|
|
376
|
+
* @template S - The input string type (literal types are preserved).
|
|
378
377
|
* @param minLength - The minimum number of characters (inclusive).
|
|
379
378
|
* @param maxLength - The maximum number of characters (inclusive).
|
|
379
|
+
* @param s - The string to cast.
|
|
380
380
|
* @returns `s` typed as `BoundedLengthString<MinLength, MaxLength> & S`.
|
|
381
381
|
* @throws {TypeError} If `s.length < minLength` or `s.length > maxLength`.
|
|
382
382
|
*/
|
|
383
383
|
export function asBoundedLengthString<
|
|
384
|
-
S extends string,
|
|
385
384
|
MinLength extends SupportedLength,
|
|
386
385
|
MaxLength extends SupportedLength,
|
|
386
|
+
S extends string,
|
|
387
387
|
>(
|
|
388
|
-
s: S,
|
|
389
388
|
minLength: MinLength,
|
|
390
389
|
maxLength: MaxLength,
|
|
390
|
+
s: S,
|
|
391
391
|
): BoundedLengthString<MinLength, MaxLength> & S;
|
|
392
392
|
|
|
393
393
|
// Curried version
|
|
@@ -402,9 +402,9 @@ export namespace Str {
|
|
|
402
402
|
export function asBoundedLengthString(
|
|
403
403
|
...args:
|
|
404
404
|
| readonly [
|
|
405
|
-
s: string,
|
|
406
405
|
minLength: SupportedLength,
|
|
407
406
|
maxLength: SupportedLength,
|
|
407
|
+
s: string,
|
|
408
408
|
]
|
|
409
409
|
| readonly [minLength: SupportedLength, maxLength: SupportedLength]
|
|
410
410
|
): string | ((s: string) => string) {
|
|
@@ -413,18 +413,18 @@ export namespace Str {
|
|
|
413
413
|
return asBoundedLengthStringImpl(...args);
|
|
414
414
|
|
|
415
415
|
case 2:
|
|
416
|
-
return (s) => asBoundedLengthStringImpl(
|
|
416
|
+
return (s) => asBoundedLengthStringImpl(...args, s);
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
const asBoundedLengthStringImpl = (
|
|
421
|
-
s: string,
|
|
422
421
|
minLength: SupportedLength,
|
|
423
422
|
maxLength: SupportedLength,
|
|
423
|
+
s: string,
|
|
424
424
|
): string => {
|
|
425
425
|
const actualLength = s.length;
|
|
426
426
|
|
|
427
|
-
if (!isBoundedLengthString(
|
|
427
|
+
if (!isBoundedLengthString(minLength, maxLength, s)) {
|
|
428
428
|
throw new TypeError(
|
|
429
429
|
`Expected a string of length in [${minLength}, ${maxLength}], got a string of length ${actualLength}`,
|
|
430
430
|
);
|
|
@@ -447,7 +447,7 @@ export namespace Str {
|
|
|
447
447
|
* @example
|
|
448
448
|
*
|
|
449
449
|
* ```ts
|
|
450
|
-
* const countryCode = Str.asFixedLengthString('JP'
|
|
450
|
+
* const countryCode = Str.asFixedLengthString(2, 'JP');
|
|
451
451
|
*
|
|
452
452
|
* const atMost5: MaxLengthString<5> = countryCode; // OK (2 <= 5)
|
|
453
453
|
*
|
|
@@ -458,20 +458,20 @@ export namespace Str {
|
|
|
458
458
|
*
|
|
459
459
|
* assert.strictEqual(asCountryCode('US'), 'US');
|
|
460
460
|
*
|
|
461
|
-
* assert.throws(() => Str.asFixedLengthString('JP'
|
|
461
|
+
* assert.throws(() => Str.asFixedLengthString(3, 'JP')); // length 2 !== 3
|
|
462
462
|
* ```
|
|
463
463
|
*
|
|
464
|
-
* @template S - The input string type (literal types are preserved).
|
|
465
464
|
* @template Length - The exact number of characters.
|
|
466
|
-
* @
|
|
465
|
+
* @template S - The input string type (literal types are preserved).
|
|
467
466
|
* @param length - The exact number of characters.
|
|
467
|
+
* @param s - The string to cast.
|
|
468
468
|
* @returns `s` typed as `FixedLengthString<Length> & S`.
|
|
469
469
|
* @throws {TypeError} If `s.length !== length`.
|
|
470
470
|
*/
|
|
471
471
|
export function asFixedLengthString<
|
|
472
|
-
S extends string,
|
|
473
472
|
Length extends SupportedLength,
|
|
474
|
-
|
|
473
|
+
S extends string,
|
|
474
|
+
>(length: Length, s: S): FixedLengthString<Length> & S;
|
|
475
475
|
|
|
476
476
|
// Curried version
|
|
477
477
|
export function asFixedLengthString<Length extends SupportedLength>(
|
|
@@ -480,7 +480,7 @@ export namespace Str {
|
|
|
480
480
|
|
|
481
481
|
export function asFixedLengthString(
|
|
482
482
|
...args:
|
|
483
|
-
| readonly [
|
|
483
|
+
| readonly [length: SupportedLength, s: string]
|
|
484
484
|
| readonly [length: SupportedLength]
|
|
485
485
|
): string | ((s: string) => string) {
|
|
486
486
|
switch (args.length) {
|
|
@@ -488,17 +488,17 @@ export namespace Str {
|
|
|
488
488
|
return asFixedLengthStringImpl(...args);
|
|
489
489
|
|
|
490
490
|
case 1:
|
|
491
|
-
return (s) => asFixedLengthStringImpl(
|
|
491
|
+
return (s) => asFixedLengthStringImpl(args[0], s);
|
|
492
492
|
}
|
|
493
493
|
}
|
|
494
494
|
|
|
495
495
|
const asFixedLengthStringImpl = (
|
|
496
|
-
s: string,
|
|
497
496
|
length: SupportedLength,
|
|
497
|
+
s: string,
|
|
498
498
|
): string => {
|
|
499
499
|
const actualLength = s.length;
|
|
500
500
|
|
|
501
|
-
if (!isFixedLengthString(
|
|
501
|
+
if (!isFixedLengthString(length, s)) {
|
|
502
502
|
throw new TypeError(
|
|
503
503
|
`Expected a string of length ${length}, got a string of length ${actualLength}`,
|
|
504
504
|
);
|
|
@@ -537,7 +537,7 @@ export namespace Str {
|
|
|
537
537
|
export const asNonEmptyString = <S extends string>(
|
|
538
538
|
s: S,
|
|
539
539
|
): NonEmptyString & S => {
|
|
540
|
-
if (!isMinLengthString(
|
|
540
|
+
if (!isMinLengthString(1, s)) {
|
|
541
541
|
throw new TypeError('Expected a non-empty string, got an empty string');
|
|
542
542
|
}
|
|
543
543
|
|
package/src/string/str.test.mts
CHANGED
|
@@ -10,31 +10,31 @@ import { Str } from './str.mjs';
|
|
|
10
10
|
|
|
11
11
|
describe(Str.isMinLengthString, () => {
|
|
12
12
|
test('should return true when the string is long enough', () => {
|
|
13
|
-
assert.isTrue(Str.isMinLengthString('hello'
|
|
13
|
+
assert.isTrue(Str.isMinLengthString(3, 'hello'));
|
|
14
14
|
|
|
15
|
-
assert.isTrue(Str.isMinLengthString('hello'
|
|
15
|
+
assert.isTrue(Str.isMinLengthString(5, 'hello'));
|
|
16
16
|
|
|
17
|
-
assert.isTrue(Str.isMinLengthString(''
|
|
17
|
+
assert.isTrue(Str.isMinLengthString(0, ''));
|
|
18
18
|
|
|
19
|
-
assert.isTrue(Str.isMinLengthString(' '
|
|
19
|
+
assert.isTrue(Str.isMinLengthString(1, ' ')); // Space counts
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
test('should return false when the string is too short', () => {
|
|
23
|
-
assert.isFalse(Str.isMinLengthString('hi'
|
|
23
|
+
assert.isFalse(Str.isMinLengthString(3, 'hi'));
|
|
24
24
|
|
|
25
|
-
assert.isFalse(Str.isMinLengthString(''
|
|
25
|
+
assert.isFalse(Str.isMinLengthString(1, ''));
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
test('should measure length in UTF-16 code units', () => {
|
|
29
|
-
assert.isTrue(Str.isMinLengthString('🎉'
|
|
29
|
+
assert.isTrue(Str.isMinLengthString(2, '🎉')); // Surrogate pair
|
|
30
30
|
|
|
31
|
-
assert.isFalse(Str.isMinLengthString('🎉'
|
|
31
|
+
assert.isFalse(Str.isMinLengthString(3, '🎉'));
|
|
32
32
|
});
|
|
33
33
|
|
|
34
34
|
test('should act as a type guard', () => {
|
|
35
35
|
const value: string = 'very-secret-password';
|
|
36
36
|
|
|
37
|
-
if (Str.isMinLengthString(
|
|
37
|
+
if (Str.isMinLengthString(12, value)) {
|
|
38
38
|
expectType<typeof value, MinLengthString<12>>('<=');
|
|
39
39
|
|
|
40
40
|
expectType<typeof value, NonEmptyString>('<='); // 12 >= 1
|
|
@@ -48,7 +48,7 @@ describe(Str.isMinLengthString, () => {
|
|
|
48
48
|
test('should preserve literal string types', () => {
|
|
49
49
|
const literal = 'hello';
|
|
50
50
|
|
|
51
|
-
if (Str.isMinLengthString(
|
|
51
|
+
if (Str.isMinLengthString(3, literal)) {
|
|
52
52
|
expectType<typeof literal, 'hello'>('<=');
|
|
53
53
|
|
|
54
54
|
expectType<typeof literal, MinLengthString<3>>('<=');
|
|
@@ -58,29 +58,29 @@ describe(Str.isMinLengthString, () => {
|
|
|
58
58
|
|
|
59
59
|
describe(Str.isMaxLengthString, () => {
|
|
60
60
|
test('should return true when the string is short enough', () => {
|
|
61
|
-
assert.isTrue(Str.isMaxLengthString('hello'
|
|
61
|
+
assert.isTrue(Str.isMaxLengthString(10, 'hello'));
|
|
62
62
|
|
|
63
|
-
assert.isTrue(Str.isMaxLengthString('hello'
|
|
63
|
+
assert.isTrue(Str.isMaxLengthString(5, 'hello'));
|
|
64
64
|
|
|
65
|
-
assert.isTrue(Str.isMaxLengthString(''
|
|
65
|
+
assert.isTrue(Str.isMaxLengthString(0, ''));
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
test('should return false when the string is too long', () => {
|
|
69
|
-
assert.isFalse(Str.isMaxLengthString('hello'
|
|
69
|
+
assert.isFalse(Str.isMaxLengthString(3, 'hello'));
|
|
70
70
|
|
|
71
|
-
assert.isFalse(Str.isMaxLengthString(' '
|
|
71
|
+
assert.isFalse(Str.isMaxLengthString(0, ' '));
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
test('should measure length in UTF-16 code units', () => {
|
|
75
|
-
assert.isFalse(Str.isMaxLengthString('🎉'
|
|
75
|
+
assert.isFalse(Str.isMaxLengthString(1, '🎉')); // Surrogate pair
|
|
76
76
|
|
|
77
|
-
assert.isTrue(Str.isMaxLengthString('🎉'
|
|
77
|
+
assert.isTrue(Str.isMaxLengthString(2, '🎉'));
|
|
78
78
|
});
|
|
79
79
|
|
|
80
80
|
test('should act as a type guard', () => {
|
|
81
81
|
const value: string = 'noshiro';
|
|
82
82
|
|
|
83
|
-
if (Str.isMaxLengthString(
|
|
83
|
+
if (Str.isMaxLengthString(32, value)) {
|
|
84
84
|
expectType<typeof value, MaxLengthString<32>>('<=');
|
|
85
85
|
|
|
86
86
|
expectType<typeof value, MaxLengthString<64>>('<='); // 32 <= 64
|
|
@@ -94,25 +94,25 @@ describe(Str.isMaxLengthString, () => {
|
|
|
94
94
|
|
|
95
95
|
describe(Str.isBoundedLengthString, () => {
|
|
96
96
|
test('should return true when the length is within the range', () => {
|
|
97
|
-
assert.isTrue(Str.isBoundedLengthString('user-12345678'
|
|
97
|
+
assert.isTrue(Str.isBoundedLengthString(8, 16, 'user-12345678'));
|
|
98
98
|
|
|
99
|
-
assert.isTrue(Str.isBoundedLengthString(
|
|
99
|
+
assert.isTrue(Str.isBoundedLengthString(8, 16, '12345678')); // Lower bound (inclusive)
|
|
100
100
|
|
|
101
|
-
assert.isTrue(Str.isBoundedLengthString(
|
|
101
|
+
assert.isTrue(Str.isBoundedLengthString(8, 16, '1234567890123456')); // Upper bound (inclusive)
|
|
102
102
|
|
|
103
|
-
assert.isTrue(Str.isBoundedLengthString(
|
|
103
|
+
assert.isTrue(Str.isBoundedLengthString(0, 0, ''));
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
test('should return false when the length is out of range', () => {
|
|
107
|
-
assert.isFalse(Str.isBoundedLengthString(
|
|
107
|
+
assert.isFalse(Str.isBoundedLengthString(8, 16, 'user'));
|
|
108
108
|
|
|
109
|
-
assert.isFalse(Str.isBoundedLengthString(
|
|
109
|
+
assert.isFalse(Str.isBoundedLengthString(8, 16, '12345678901234567'));
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
test('should act as a type guard', () => {
|
|
113
113
|
const value: string = 'user-12345678';
|
|
114
114
|
|
|
115
|
-
if (Str.isBoundedLengthString(
|
|
115
|
+
if (Str.isBoundedLengthString(8, 16, value)) {
|
|
116
116
|
expectType<typeof value, BoundedLengthString<8, 16>>('<=');
|
|
117
117
|
|
|
118
118
|
expectType<typeof value, BoundedLengthString<1, 255>>('<='); // [8, 16] ⊆ [1, 255]
|
|
@@ -130,25 +130,25 @@ describe(Str.isBoundedLengthString, () => {
|
|
|
130
130
|
|
|
131
131
|
describe(Str.isFixedLengthString, () => {
|
|
132
132
|
test('should return true only for the exact length', () => {
|
|
133
|
-
assert.isTrue(Str.isFixedLengthString('JP'
|
|
133
|
+
assert.isTrue(Str.isFixedLengthString(2, 'JP'));
|
|
134
134
|
|
|
135
|
-
assert.isTrue(Str.isFixedLengthString(''
|
|
135
|
+
assert.isTrue(Str.isFixedLengthString(0, ''));
|
|
136
136
|
|
|
137
|
-
assert.isFalse(Str.isFixedLengthString('JPN'
|
|
137
|
+
assert.isFalse(Str.isFixedLengthString(2, 'JPN'));
|
|
138
138
|
|
|
139
|
-
assert.isFalse(Str.isFixedLengthString('J'
|
|
139
|
+
assert.isFalse(Str.isFixedLengthString(2, 'J'));
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
test('should measure length in UTF-16 code units', () => {
|
|
143
|
-
assert.isTrue(Str.isFixedLengthString('🎉'
|
|
143
|
+
assert.isTrue(Str.isFixedLengthString(2, '🎉')); // Surrogate pair
|
|
144
144
|
|
|
145
|
-
assert.isFalse(Str.isFixedLengthString('🎉'
|
|
145
|
+
assert.isFalse(Str.isFixedLengthString(1, '🎉'));
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
test('should act as a type guard', () => {
|
|
149
149
|
const value: string = 'JP';
|
|
150
150
|
|
|
151
|
-
if (Str.isFixedLengthString(
|
|
151
|
+
if (Str.isFixedLengthString(2, value)) {
|
|
152
152
|
expectType<typeof value, FixedLengthString<2>>('<=');
|
|
153
153
|
|
|
154
154
|
expectType<typeof value, BoundedLengthString<2, 2>>('<=');
|
|
@@ -166,7 +166,7 @@ describe(Str.isFixedLengthString, () => {
|
|
|
166
166
|
|
|
167
167
|
describe('Str length-bounded casts', () => {
|
|
168
168
|
test('asMinLengthString returns the value and narrows its type', () => {
|
|
169
|
-
const result = Str.asMinLengthString('very-secret'
|
|
169
|
+
const result = Str.asMinLengthString(3, 'very-secret');
|
|
170
170
|
|
|
171
171
|
expectType<typeof result, MinLengthString<3>>('<=');
|
|
172
172
|
|
|
@@ -174,11 +174,11 @@ describe('Str length-bounded casts', () => {
|
|
|
174
174
|
});
|
|
175
175
|
|
|
176
176
|
test('asMinLengthString throws when too short', () => {
|
|
177
|
-
assert.throws(() => Str.asMinLengthString('hi'
|
|
177
|
+
assert.throws(() => Str.asMinLengthString(3, 'hi'), TypeError);
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
test('asMaxLengthString returns the value and narrows its type', () => {
|
|
181
|
-
const result = Str.asMaxLengthString('noshiro'
|
|
181
|
+
const result = Str.asMaxLengthString(32, 'noshiro');
|
|
182
182
|
|
|
183
183
|
expectType<typeof result, MaxLengthString<32>>('<=');
|
|
184
184
|
|
|
@@ -186,11 +186,11 @@ describe('Str length-bounded casts', () => {
|
|
|
186
186
|
});
|
|
187
187
|
|
|
188
188
|
test('asMaxLengthString throws when too long', () => {
|
|
189
|
-
assert.throws(() => Str.asMaxLengthString('noshiro'
|
|
189
|
+
assert.throws(() => Str.asMaxLengthString(3, 'noshiro'), TypeError);
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
test('asBoundedLengthString returns the value and narrows its type', () => {
|
|
193
|
-
const result = Str.asBoundedLengthString('user-12345678'
|
|
193
|
+
const result = Str.asBoundedLengthString(8, 16, 'user-12345678');
|
|
194
194
|
|
|
195
195
|
expectType<typeof result, BoundedLengthString<8, 16>>('<=');
|
|
196
196
|
|
|
@@ -198,11 +198,11 @@ describe('Str length-bounded casts', () => {
|
|
|
198
198
|
});
|
|
199
199
|
|
|
200
200
|
test('asBoundedLengthString throws when out of range', () => {
|
|
201
|
-
assert.throws(() => Str.asBoundedLengthString(
|
|
201
|
+
assert.throws(() => Str.asBoundedLengthString(8, 16, 'user'), TypeError);
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
test('asFixedLengthString returns the value and narrows its type', () => {
|
|
205
|
-
const result = Str.asFixedLengthString('JP'
|
|
205
|
+
const result = Str.asFixedLengthString(2, 'JP');
|
|
206
206
|
|
|
207
207
|
expectType<typeof result, FixedLengthString<2>>('<=');
|
|
208
208
|
|
|
@@ -210,7 +210,7 @@ describe('Str length-bounded casts', () => {
|
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
test('asFixedLengthString throws when the length differs', () => {
|
|
213
|
-
assert.throws(() => Str.asFixedLengthString('JP'
|
|
213
|
+
assert.throws(() => Str.asFixedLengthString(3, 'JP'), TypeError);
|
|
214
214
|
});
|
|
215
215
|
});
|
|
216
216
|
|