ts-data-forge 13.0.0 → 14.0.1
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 +4 -3
- 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 +45 -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-slice-clamped.d.mts.map +1 -1
- package/dist/array/impl/array-utils-slice-clamped.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 +36 -19
- 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/collections/imap.d.mts +1 -1
- package/dist/collections/imap.mjs.map +1 -1
- package/dist/collections/iset-mapped.d.mts +2 -2
- package/dist/collections/iset-mapped.d.mts.map +1 -1
- package/dist/collections/iset-mapped.mjs.map +1 -1
- package/dist/collections/iset.d.mts +2 -2
- package/dist/collections/iset.d.mts.map +1 -1
- package/dist/collections/iset.mjs.map +1 -1
- package/dist/number/num-arithmetic-types.d.mts +2 -2
- package/dist/number/num-arithmetic-types.d.mts.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 +4 -5
- package/src/array/impl/array-utils-creation.mts +7 -3
- package/src/array/impl/array-utils-creation.test.mts +10 -8
- 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 +84 -61
- package/src/array/impl/array-utils-modification.test.mts +145 -6
- package/src/array/impl/array-utils-reducing-value.mts +5 -5
- package/src/array/impl/array-utils-shape-invariants.test.mts +284 -0
- package/src/array/impl/array-utils-slice-clamped.mts +2 -1
- 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 +91 -37
- package/src/array/impl/array-utils-transformation.test.mts +96 -23
- 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/imap.mts +1 -1
- package/src/collections/iset-mapped.mts +7 -3
- package/src/collections/iset.mts +7 -3
- package/src/collections/iset.test.mts +9 -6
- package/src/collections/queue.test.mts +1 -1
- package/src/number/num-arithmetic-types.mts +8 -4
- package/src/number/num.test.mts +2 -2
- package/src/string/str.mts +69 -69
- package/src/string/str.test.mts +40 -40
|
@@ -23,13 +23,14 @@ import {
|
|
|
23
23
|
* structural tuple prefix (in-range indexed access without `undefined` under
|
|
24
24
|
* `noUncheckedIndexedAccess`).
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* The length argument comes first, matching the type-parameter order of
|
|
27
|
+
* `FixedLengthArray<Length, Elm>`. Omitting the array returns a function that
|
|
28
|
+
* casts one, which is convenient for `pipe`/`map`.
|
|
28
29
|
*
|
|
29
30
|
* @example
|
|
30
31
|
*
|
|
31
32
|
* ```ts
|
|
32
|
-
* const rgb = Arr.asFixedLengthArray([255, 128, 0]
|
|
33
|
+
* const rgb = Arr.asFixedLengthArray(3, [255, 128, 0]);
|
|
33
34
|
*
|
|
34
35
|
* const atMost5: MaxLengthArray<5, number> = rgb; // OK (3 <= 5)
|
|
35
36
|
* const red: number = rgb[0]; // OK — no `undefined`
|
|
@@ -38,20 +39,20 @@ import {
|
|
|
38
39
|
* const asRgb = Arr.asFixedLengthArray(3);
|
|
39
40
|
* const green = asRgb([0, 255, 0]);
|
|
40
41
|
*
|
|
41
|
-
* // Arr.asFixedLengthArray([255, 128]
|
|
42
|
+
* // Arr.asFixedLengthArray(3, [255, 128]); // throws TypeError
|
|
42
43
|
* ```
|
|
43
44
|
*
|
|
44
|
-
* @template Xs - The input array type (tuple types are preserved).
|
|
45
45
|
* @template Length - The exact number of elements.
|
|
46
|
-
* @
|
|
46
|
+
* @template Xs - The input array type (tuple types are preserved).
|
|
47
47
|
* @param length - The exact number of elements.
|
|
48
|
+
* @param xs - The array to cast.
|
|
48
49
|
* @returns `xs` typed as `FixedLengthArray<Length, Xs[number]> & Xs`.
|
|
49
50
|
* @throws {TypeError} If `xs.length !== length`.
|
|
50
51
|
*/
|
|
51
52
|
export function asFixedLengthArray<
|
|
52
|
-
Xs extends readonly unknown[],
|
|
53
53
|
Length extends SupportedLength,
|
|
54
|
-
|
|
54
|
+
Xs extends readonly unknown[],
|
|
55
|
+
>(length: Length, xs: Xs): FixedLengthArray<Length, Xs[number]> & Xs;
|
|
55
56
|
|
|
56
57
|
// Curried version
|
|
57
58
|
export function asFixedLengthArray<Length extends SupportedLength>(
|
|
@@ -62,7 +63,7 @@ export function asFixedLengthArray<Length extends SupportedLength>(
|
|
|
62
63
|
|
|
63
64
|
export function asFixedLengthArray<E>(
|
|
64
65
|
...args:
|
|
65
|
-
| readonly [xs: readonly E[]
|
|
66
|
+
| readonly [length: SupportedLength, xs: readonly E[]]
|
|
66
67
|
| readonly [length: SupportedLength]
|
|
67
68
|
): readonly E[] | ((xs: readonly E[]) => readonly E[]) {
|
|
68
69
|
switch (args.length) {
|
|
@@ -70,17 +71,17 @@ export function asFixedLengthArray<E>(
|
|
|
70
71
|
return asFixedLengthArrayImpl(...args);
|
|
71
72
|
|
|
72
73
|
case 1:
|
|
73
|
-
return (xs) => asFixedLengthArrayImpl(
|
|
74
|
+
return (xs) => asFixedLengthArrayImpl(args[0], xs);
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
const asFixedLengthArrayImpl = <E,>(
|
|
78
|
-
xs: readonly E[],
|
|
79
79
|
length: SupportedLength,
|
|
80
|
+
xs: readonly E[],
|
|
80
81
|
): readonly E[] => {
|
|
81
82
|
const actualLength = xs.length;
|
|
82
83
|
|
|
83
|
-
if (!isFixedLengthArray(
|
|
84
|
+
if (!isFixedLengthArray(length, xs)) {
|
|
84
85
|
throw new TypeError(
|
|
85
86
|
`Expected an array of length ${length}, got an array of length ${actualLength}`,
|
|
86
87
|
);
|
|
@@ -99,13 +100,14 @@ const asFixedLengthArrayImpl = <E,>(
|
|
|
99
100
|
* structural tuple prefix (indexed access below `min(N, 10)` without
|
|
100
101
|
* `undefined` under `noUncheckedIndexedAccess`).
|
|
101
102
|
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
103
|
+
* The length argument comes first, matching the type-parameter order of
|
|
104
|
+
* `MinLengthArray<MinLength, Elm>`. Omitting the array returns a function that
|
|
105
|
+
* casts one, which is convenient for `pipe`/`map`.
|
|
104
106
|
*
|
|
105
107
|
* @example
|
|
106
108
|
*
|
|
107
109
|
* ```ts
|
|
108
|
-
* const history = Arr.asMinLengthArray([0, 1, 2, 3]
|
|
110
|
+
* const history = Arr.asMinLengthArray(3, [0, 1, 2, 3]);
|
|
109
111
|
*
|
|
110
112
|
* const nonEmpty: MinLengthArray<1, number> = history; // OK (3 >= 1)
|
|
111
113
|
* const first: number = history[0]; // OK — no `undefined`
|
|
@@ -114,20 +116,20 @@ const asFixedLengthArrayImpl = <E,>(
|
|
|
114
116
|
* const asHistory = Arr.asMinLengthArray(3);
|
|
115
117
|
* const next = asHistory([4, 5, 6, 7]);
|
|
116
118
|
*
|
|
117
|
-
* // Arr.asMinLengthArray([0]
|
|
119
|
+
* // Arr.asMinLengthArray(3, [0]); // throws TypeError
|
|
118
120
|
* ```
|
|
119
121
|
*
|
|
120
|
-
* @template Xs - The input array type (tuple types are preserved).
|
|
121
122
|
* @template MinLength - The minimum number of elements (inclusive).
|
|
122
|
-
* @
|
|
123
|
+
* @template Xs - The input array type (tuple types are preserved).
|
|
123
124
|
* @param minLength - The minimum number of elements (inclusive).
|
|
125
|
+
* @param xs - The array to cast.
|
|
124
126
|
* @returns `xs` typed as `MinLengthArray<MinLength, Xs[number]> & Xs`.
|
|
125
127
|
* @throws {TypeError} If `xs.length < minLength`.
|
|
126
128
|
*/
|
|
127
129
|
export function asMinLengthArray<
|
|
128
|
-
Xs extends readonly unknown[],
|
|
129
130
|
MinLength extends SupportedLength,
|
|
130
|
-
|
|
131
|
+
Xs extends readonly unknown[],
|
|
132
|
+
>(minLength: MinLength, xs: Xs): MinLengthArray<MinLength, Xs[number]> & Xs;
|
|
131
133
|
|
|
132
134
|
// Curried version
|
|
133
135
|
export function asMinLengthArray<MinLength extends SupportedLength>(
|
|
@@ -138,7 +140,7 @@ export function asMinLengthArray<MinLength extends SupportedLength>(
|
|
|
138
140
|
|
|
139
141
|
export function asMinLengthArray<E>(
|
|
140
142
|
...args:
|
|
141
|
-
| readonly [xs: readonly E[]
|
|
143
|
+
| readonly [minLength: SupportedLength, xs: readonly E[]]
|
|
142
144
|
| readonly [minLength: SupportedLength]
|
|
143
145
|
): readonly E[] | ((xs: readonly E[]) => readonly E[]) {
|
|
144
146
|
switch (args.length) {
|
|
@@ -146,17 +148,17 @@ export function asMinLengthArray<E>(
|
|
|
146
148
|
return asMinLengthArrayImpl(...args);
|
|
147
149
|
|
|
148
150
|
case 1:
|
|
149
|
-
return (xs) => asMinLengthArrayImpl(
|
|
151
|
+
return (xs) => asMinLengthArrayImpl(args[0], xs);
|
|
150
152
|
}
|
|
151
153
|
}
|
|
152
154
|
|
|
153
155
|
const asMinLengthArrayImpl = <E,>(
|
|
154
|
-
xs: readonly E[],
|
|
155
156
|
minLength: SupportedLength,
|
|
157
|
+
xs: readonly E[],
|
|
156
158
|
): readonly E[] => {
|
|
157
159
|
const actualLength = xs.length;
|
|
158
160
|
|
|
159
|
-
if (!isMinLengthArray(
|
|
161
|
+
if (!isMinLengthArray(minLength, xs)) {
|
|
160
162
|
throw new TypeError(
|
|
161
163
|
`Expected an array of length >= ${minLength}, got an array of length ${actualLength}`,
|
|
162
164
|
);
|
|
@@ -173,13 +175,14 @@ const asMinLengthArrayImpl = <E,>(
|
|
|
173
175
|
* length is verified at runtime, and the original array type (e.g. tuple
|
|
174
176
|
* types) is preserved via intersection.
|
|
175
177
|
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
+
* The length argument comes first, matching the type-parameter order of
|
|
179
|
+
* `MaxLengthArray<MaxLength, Elm>`. Omitting the array returns a function that
|
|
180
|
+
* casts one, which is convenient for `pipe`/`map`.
|
|
178
181
|
*
|
|
179
182
|
* @example
|
|
180
183
|
*
|
|
181
184
|
* ```ts
|
|
182
|
-
* const tags = Arr.asMaxLengthArray(['a', 'b', 'c']
|
|
185
|
+
* const tags = Arr.asMaxLengthArray(8, ['a', 'b', 'c']);
|
|
183
186
|
*
|
|
184
187
|
* const relaxed: MaxLengthArray<16, string> = tags; // OK (8 <= 16)
|
|
185
188
|
*
|
|
@@ -187,20 +190,20 @@ const asMinLengthArrayImpl = <E,>(
|
|
|
187
190
|
* const asTags = Arr.asMaxLengthArray(8);
|
|
188
191
|
* const more = asTags(['d', 'e']);
|
|
189
192
|
*
|
|
190
|
-
* // Arr.asMaxLengthArray(['a', 'b', 'c']
|
|
193
|
+
* // Arr.asMaxLengthArray(2, ['a', 'b', 'c']); // throws TypeError
|
|
191
194
|
* ```
|
|
192
195
|
*
|
|
193
|
-
* @template Xs - The input array type (tuple types are preserved).
|
|
194
196
|
* @template MaxLength - The maximum number of elements (inclusive).
|
|
195
|
-
* @
|
|
197
|
+
* @template Xs - The input array type (tuple types are preserved).
|
|
196
198
|
* @param maxLength - The maximum number of elements (inclusive).
|
|
199
|
+
* @param xs - The array to cast.
|
|
197
200
|
* @returns `xs` typed as `MaxLengthArray<MaxLength, Xs[number]> & Xs`.
|
|
198
201
|
* @throws {TypeError} If `xs.length > maxLength`.
|
|
199
202
|
*/
|
|
200
203
|
export function asMaxLengthArray<
|
|
201
|
-
Xs extends readonly unknown[],
|
|
202
204
|
MaxLength extends SupportedLength,
|
|
203
|
-
|
|
205
|
+
Xs extends readonly unknown[],
|
|
206
|
+
>(maxLength: MaxLength, xs: Xs): MaxLengthArray<MaxLength, Xs[number]> & Xs;
|
|
204
207
|
|
|
205
208
|
// Curried version
|
|
206
209
|
export function asMaxLengthArray<MaxLength extends SupportedLength>(
|
|
@@ -211,7 +214,7 @@ export function asMaxLengthArray<MaxLength extends SupportedLength>(
|
|
|
211
214
|
|
|
212
215
|
export function asMaxLengthArray<E>(
|
|
213
216
|
...args:
|
|
214
|
-
| readonly [xs: readonly E[]
|
|
217
|
+
| readonly [maxLength: SupportedLength, xs: readonly E[]]
|
|
215
218
|
| readonly [maxLength: SupportedLength]
|
|
216
219
|
): readonly E[] | ((xs: readonly E[]) => readonly E[]) {
|
|
217
220
|
switch (args.length) {
|
|
@@ -219,17 +222,17 @@ export function asMaxLengthArray<E>(
|
|
|
219
222
|
return asMaxLengthArrayImpl(...args);
|
|
220
223
|
|
|
221
224
|
case 1:
|
|
222
|
-
return (xs) => asMaxLengthArrayImpl(
|
|
225
|
+
return (xs) => asMaxLengthArrayImpl(args[0], xs);
|
|
223
226
|
}
|
|
224
227
|
}
|
|
225
228
|
|
|
226
229
|
const asMaxLengthArrayImpl = <E,>(
|
|
227
|
-
xs: readonly E[],
|
|
228
230
|
maxLength: SupportedLength,
|
|
231
|
+
xs: readonly E[],
|
|
229
232
|
): readonly E[] => {
|
|
230
233
|
const actualLength = xs.length;
|
|
231
234
|
|
|
232
|
-
if (!isMaxLengthArray(
|
|
235
|
+
if (!isMaxLengthArray(maxLength, xs)) {
|
|
233
236
|
throw new TypeError(
|
|
234
237
|
`Expected an array of length <= ${maxLength}, got an array of length ${actualLength}`,
|
|
235
238
|
);
|
|
@@ -248,14 +251,14 @@ const asMaxLengthArrayImpl = <E,>(
|
|
|
248
251
|
* at runtime, and the original array type (e.g. tuple types) is preserved via
|
|
249
252
|
* intersection.
|
|
250
253
|
*
|
|
251
|
-
*
|
|
252
|
-
* `
|
|
253
|
-
* `pipe`/`map`.
|
|
254
|
+
* The length arguments come first, matching the type-parameter order of
|
|
255
|
+
* `BoundedLengthArray<MinLength, MaxLength, Elm>`. Omitting the array returns a
|
|
256
|
+
* function that casts one, which is convenient for `pipe`/`map`.
|
|
254
257
|
*
|
|
255
258
|
* @example
|
|
256
259
|
*
|
|
257
260
|
* ```ts
|
|
258
|
-
* const selection = Arr.asBoundedLengthArray([1, 2, 3]
|
|
261
|
+
* const selection = Arr.asBoundedLengthArray(1, 5, [1, 2, 3]);
|
|
259
262
|
*
|
|
260
263
|
* const relaxed: BoundedLengthArray<0, 100, number> = selection; // OK
|
|
261
264
|
*
|
|
@@ -263,27 +266,27 @@ const asMaxLengthArrayImpl = <E,>(
|
|
|
263
266
|
* const asSelection = Arr.asBoundedLengthArray(1, 5);
|
|
264
267
|
* const next = asSelection([4, 5]);
|
|
265
268
|
*
|
|
266
|
-
* // Arr.asBoundedLengthArray(
|
|
269
|
+
* // Arr.asBoundedLengthArray(1, 5, []); // throws TypeError
|
|
267
270
|
* ```
|
|
268
271
|
*
|
|
269
|
-
* @template Xs - The input array type (tuple types are preserved).
|
|
270
272
|
* @template MinLength - The minimum number of elements (inclusive).
|
|
271
273
|
* @template MaxLength - The maximum number of elements (inclusive).
|
|
272
|
-
* @
|
|
274
|
+
* @template Xs - The input array type (tuple types are preserved).
|
|
273
275
|
* @param minLength - The minimum number of elements (inclusive).
|
|
274
276
|
* @param maxLength - The maximum number of elements (inclusive).
|
|
277
|
+
* @param xs - The array to cast.
|
|
275
278
|
* @returns `xs` typed as
|
|
276
279
|
* `BoundedLengthArray<MinLength, MaxLength, Xs[number]> & Xs`.
|
|
277
280
|
* @throws {TypeError} If `xs.length < minLength || xs.length > maxLength`.
|
|
278
281
|
*/
|
|
279
282
|
export function asBoundedLengthArray<
|
|
280
|
-
Xs extends readonly unknown[],
|
|
281
283
|
MinLength extends SupportedLength,
|
|
282
284
|
MaxLength extends SupportedLength,
|
|
285
|
+
Xs extends readonly unknown[],
|
|
283
286
|
>(
|
|
284
|
-
xs: Xs,
|
|
285
287
|
minLength: MinLength,
|
|
286
288
|
maxLength: MaxLength,
|
|
289
|
+
xs: Xs,
|
|
287
290
|
): BoundedLengthArray<MinLength, MaxLength, Xs[number]> & Xs;
|
|
288
291
|
|
|
289
292
|
// Curried version
|
|
@@ -300,9 +303,9 @@ export function asBoundedLengthArray<
|
|
|
300
303
|
export function asBoundedLengthArray<E>(
|
|
301
304
|
...args:
|
|
302
305
|
| readonly [
|
|
303
|
-
xs: readonly E[],
|
|
304
306
|
minLength: SupportedLength,
|
|
305
307
|
maxLength: SupportedLength,
|
|
308
|
+
xs: readonly E[],
|
|
306
309
|
]
|
|
307
310
|
| readonly [minLength: SupportedLength, maxLength: SupportedLength]
|
|
308
311
|
): readonly E[] | ((xs: readonly E[]) => readonly E[]) {
|
|
@@ -311,18 +314,18 @@ export function asBoundedLengthArray<E>(
|
|
|
311
314
|
return asBoundedLengthArrayImpl(...args);
|
|
312
315
|
|
|
313
316
|
case 2:
|
|
314
|
-
return (xs) => asBoundedLengthArrayImpl(
|
|
317
|
+
return (xs) => asBoundedLengthArrayImpl(args[0], args[1], xs);
|
|
315
318
|
}
|
|
316
319
|
}
|
|
317
320
|
|
|
318
321
|
const asBoundedLengthArrayImpl = <E,>(
|
|
319
|
-
xs: readonly E[],
|
|
320
322
|
minLength: SupportedLength,
|
|
321
323
|
maxLength: SupportedLength,
|
|
324
|
+
xs: readonly E[],
|
|
322
325
|
): readonly E[] => {
|
|
323
326
|
const actualLength = xs.length;
|
|
324
327
|
|
|
325
|
-
if (!isBoundedLengthArray(
|
|
328
|
+
if (!isBoundedLengthArray(minLength, maxLength, xs)) {
|
|
326
329
|
throw new TypeError(
|
|
327
330
|
`Expected an array of length in [${minLength}, ${maxLength}], got an array of length ${actualLength}`,
|
|
328
331
|
);
|
|
@@ -331,6 +334,41 @@ const asBoundedLengthArrayImpl = <E,>(
|
|
|
331
334
|
return xs;
|
|
332
335
|
};
|
|
333
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Casts an array to `FixedLengthArray<0, E>` after checking that it is empty.
|
|
339
|
+
*
|
|
340
|
+
* `Arr.isEmpty` narrows to that same type, so this is its cast counterpart —
|
|
341
|
+
* the length-0 specialization of {@link asFixedLengthArray}. Use this instead
|
|
342
|
+
* of writing `xs as unknown as FixedLengthArray<0, E>`: the length is verified
|
|
343
|
+
* at runtime, and the original array type is preserved via intersection.
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
*
|
|
347
|
+
* ```ts
|
|
348
|
+
* const nothing = Arr.asEmptyArray([]);
|
|
349
|
+
*
|
|
350
|
+
* const atMost5: MaxLengthArray<5, never> = nothing; // OK (0 <= 5)
|
|
351
|
+
*
|
|
352
|
+
* // Arr.asEmptyArray([1]); // throws TypeError
|
|
353
|
+
* ```
|
|
354
|
+
*
|
|
355
|
+
* @template Xs - The input array type (tuple types are preserved).
|
|
356
|
+
* @param xs - The array to cast.
|
|
357
|
+
* @returns `xs` typed as `FixedLengthArray<0, Xs[number]> & Xs`.
|
|
358
|
+
* @throws {TypeError} If `xs` is not empty.
|
|
359
|
+
*/
|
|
360
|
+
export const asEmptyArray = <Xs extends readonly unknown[]>(
|
|
361
|
+
xs: Xs,
|
|
362
|
+
): FixedLengthArray<0, Xs[number]> & Xs => {
|
|
363
|
+
if (!isFixedLengthArray(0, xs)) {
|
|
364
|
+
throw new TypeError(
|
|
365
|
+
`Expected an empty array, got an array of length ${xs.length}`,
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return xs;
|
|
370
|
+
};
|
|
371
|
+
|
|
334
372
|
/**
|
|
335
373
|
* Casts an array to `NonEmptyArray<E>` after checking that it has at least one
|
|
336
374
|
* element.
|
|
@@ -360,7 +398,7 @@ const asBoundedLengthArrayImpl = <E,>(
|
|
|
360
398
|
export const asNonEmptyArray = <Xs extends readonly unknown[]>(
|
|
361
399
|
xs: Xs,
|
|
362
400
|
): NonEmptyArray<Xs[number]> & Xs => {
|
|
363
|
-
if (!isMinLengthArray(
|
|
401
|
+
if (!isMinLengthArray(1, xs)) {
|
|
364
402
|
throw new TypeError('Expected a non-empty array, got an empty array');
|
|
365
403
|
}
|
|
366
404
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
import { expectType } from '../../expect-type.mjs';
|
|
9
9
|
import {
|
|
10
10
|
asBoundedLengthArray,
|
|
11
|
+
asEmptyArray,
|
|
11
12
|
asFixedLengthArray,
|
|
12
13
|
asMaxLengthArray,
|
|
13
14
|
asMinLengthArray,
|
|
@@ -18,7 +19,7 @@ describe(asFixedLengthArray, () => {
|
|
|
18
19
|
test('should return the input when the length matches exactly', () => {
|
|
19
20
|
const xs: readonly number[] = [0, 1, 2, 3] as const;
|
|
20
21
|
|
|
21
|
-
const casted = asFixedLengthArray(
|
|
22
|
+
const casted = asFixedLengthArray(4, xs);
|
|
22
23
|
|
|
23
24
|
expect(casted).toBe(xs);
|
|
24
25
|
|
|
@@ -31,9 +32,9 @@ describe(asFixedLengthArray, () => {
|
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
test('should throw when the length does not match', () => {
|
|
34
|
-
expect(() => asFixedLengthArray([0, 1, 2]
|
|
35
|
+
expect(() => asFixedLengthArray(4, [0, 1, 2])).toThrow(TypeError);
|
|
35
36
|
|
|
36
|
-
expect(() => asFixedLengthArray([0, 1, 2]
|
|
37
|
+
expect(() => asFixedLengthArray(4, [0, 1, 2])).toThrow(
|
|
37
38
|
'Expected an array of length 4, got an array of length 3',
|
|
38
39
|
);
|
|
39
40
|
});
|
|
@@ -41,7 +42,7 @@ describe(asFixedLengthArray, () => {
|
|
|
41
42
|
test('should preserve the original array type', () => {
|
|
42
43
|
const tuple = [1, 2, 3] as const;
|
|
43
44
|
|
|
44
|
-
const casted = asFixedLengthArray(
|
|
45
|
+
const casted = asFixedLengthArray(3, tuple);
|
|
45
46
|
|
|
46
47
|
expect(casted).toBe(tuple);
|
|
47
48
|
|
|
@@ -78,7 +79,7 @@ describe(asMinLengthArray, () => {
|
|
|
78
79
|
test('should return the input when the array is long enough', () => {
|
|
79
80
|
const xs: readonly number[] = [0, 1, 2, 3] as const;
|
|
80
81
|
|
|
81
|
-
const casted = asMinLengthArray(
|
|
82
|
+
const casted = asMinLengthArray(3, xs);
|
|
82
83
|
|
|
83
84
|
expect(casted).toBe(xs);
|
|
84
85
|
|
|
@@ -88,9 +89,9 @@ describe(asMinLengthArray, () => {
|
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
test('should throw when the array is too short', () => {
|
|
91
|
-
expect(() => asMinLengthArray([0]
|
|
92
|
+
expect(() => asMinLengthArray(3, [0])).toThrow(TypeError);
|
|
92
93
|
|
|
93
|
-
expect(() => asMinLengthArray([0]
|
|
94
|
+
expect(() => asMinLengthArray(3, [0])).toThrow(
|
|
94
95
|
'Expected an array of length >= 3, got an array of length 1',
|
|
95
96
|
);
|
|
96
97
|
});
|
|
@@ -120,7 +121,7 @@ describe(asMaxLengthArray, () => {
|
|
|
120
121
|
test('should return the input when the array is short enough', () => {
|
|
121
122
|
const xs: readonly number[] = [0, 1, 2] as const;
|
|
122
123
|
|
|
123
|
-
const casted = asMaxLengthArray(
|
|
124
|
+
const casted = asMaxLengthArray(8, xs);
|
|
124
125
|
|
|
125
126
|
expect(casted).toBe(xs);
|
|
126
127
|
|
|
@@ -130,9 +131,9 @@ describe(asMaxLengthArray, () => {
|
|
|
130
131
|
});
|
|
131
132
|
|
|
132
133
|
test('should throw when the array is too long', () => {
|
|
133
|
-
expect(() => asMaxLengthArray([0, 1, 2]
|
|
134
|
+
expect(() => asMaxLengthArray(2, [0, 1, 2])).toThrow(TypeError);
|
|
134
135
|
|
|
135
|
-
expect(() => asMaxLengthArray([0, 1, 2]
|
|
136
|
+
expect(() => asMaxLengthArray(2, [0, 1, 2])).toThrow(
|
|
136
137
|
'Expected an array of length <= 2, got an array of length 3',
|
|
137
138
|
);
|
|
138
139
|
});
|
|
@@ -162,7 +163,7 @@ describe(asBoundedLengthArray, () => {
|
|
|
162
163
|
test('should return the input when the length is within the range', () => {
|
|
163
164
|
const xs: readonly number[] = [1, 2, 3] as const;
|
|
164
165
|
|
|
165
|
-
const casted = asBoundedLengthArray(
|
|
166
|
+
const casted = asBoundedLengthArray(1, 5, xs);
|
|
166
167
|
|
|
167
168
|
expect(casted).toBe(xs);
|
|
168
169
|
|
|
@@ -174,9 +175,9 @@ describe(asBoundedLengthArray, () => {
|
|
|
174
175
|
});
|
|
175
176
|
|
|
176
177
|
test('should throw when the length is out of the range', () => {
|
|
177
|
-
expect(() => asBoundedLengthArray(
|
|
178
|
+
expect(() => asBoundedLengthArray(1, 5, [])).toThrow(TypeError);
|
|
178
179
|
|
|
179
|
-
expect(() => asBoundedLengthArray([1, 2, 3, 4, 5, 6]
|
|
180
|
+
expect(() => asBoundedLengthArray(1, 5, [1, 2, 3, 4, 5, 6])).toThrow(
|
|
180
181
|
'Expected an array of length in [1, 5], got an array of length 6',
|
|
181
182
|
);
|
|
182
183
|
});
|
|
@@ -242,3 +243,29 @@ describe(asNonEmptyArray, () => {
|
|
|
242
243
|
expectType<typeof casted, NonEmptyArray<1 | 2 | 3>>('<=');
|
|
243
244
|
});
|
|
244
245
|
});
|
|
246
|
+
|
|
247
|
+
describe(asEmptyArray, () => {
|
|
248
|
+
test('casts an empty array to the branded empty type', () => {
|
|
249
|
+
const values: readonly number[] = [] as const;
|
|
250
|
+
|
|
251
|
+
const casted = asEmptyArray(values);
|
|
252
|
+
|
|
253
|
+
expectType<typeof casted, FixedLengthArray<0, number>>('<=');
|
|
254
|
+
|
|
255
|
+
assert.deepStrictEqual<readonly number[]>(casted, []);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('is the same value it was handed', () => {
|
|
259
|
+
const values: readonly number[] = [] as const;
|
|
260
|
+
|
|
261
|
+
expect(asEmptyArray(values)).toBe(values);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test('throws when the array is not empty', () => {
|
|
265
|
+
expect(() => asEmptyArray([1])).toThrow(TypeError);
|
|
266
|
+
|
|
267
|
+
expect(() => asEmptyArray([1])).toThrow(
|
|
268
|
+
'Expected an empty array, got an array of length 1',
|
|
269
|
+
);
|
|
270
|
+
});
|
|
271
|
+
});
|