ts-data-forge 14.9.0 → 14.10.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.
Files changed (94) hide show
  1. package/README.md +5 -5
  2. package/dist/array/impl/array-utils-element-access.d.mts.map +1 -1
  3. package/dist/array/impl/array-utils-element-access.mjs +4 -4
  4. package/dist/array/impl/array-utils-element-access.mjs.map +1 -1
  5. package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts +8 -8
  6. package/dist/array/impl/array-utils-length-bounded-array-cast.d.mts.map +1 -1
  7. package/dist/array/impl/array-utils-length-bounded-array-cast.mjs +28 -28
  8. package/dist/array/impl/array-utils-length-bounded-array-cast.mjs.map +1 -1
  9. package/dist/array/impl/array-utils-length-bounded-array-guard.d.mts.map +1 -1
  10. package/dist/array/impl/array-utils-length-bounded-array-guard.mjs +4 -4
  11. package/dist/array/impl/array-utils-length-bounded-array-guard.mjs.map +1 -1
  12. package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts +3 -4
  13. package/dist/array/impl/array-utils-length-bounded-tuple-cast.d.mts.map +1 -1
  14. package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs +28 -29
  15. package/dist/array/impl/array-utils-length-bounded-tuple-cast.mjs.map +1 -1
  16. package/dist/array/impl/array-utils-length-bounded-tuple-guard.d.mts.map +1 -1
  17. package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs +3 -3
  18. package/dist/array/impl/array-utils-length-bounded-tuple-guard.mjs.map +1 -1
  19. package/dist/array/impl/array-utils-modification.d.mts +1 -1
  20. package/dist/array/impl/array-utils-modification.d.mts.map +1 -1
  21. package/dist/array/impl/array-utils-modification.mjs +15 -15
  22. package/dist/array/impl/array-utils-modification.mjs.map +1 -1
  23. package/dist/array/impl/array-utils-reducing-value.d.mts.map +1 -1
  24. package/dist/array/impl/array-utils-reducing-value.mjs +12 -12
  25. package/dist/array/impl/array-utils-reducing-value.mjs.map +1 -1
  26. package/dist/array/impl/array-utils-search.d.mts.map +1 -1
  27. package/dist/array/impl/array-utils-search.mjs +56 -56
  28. package/dist/array/impl/array-utils-search.mjs.map +1 -1
  29. package/dist/array/impl/array-utils-slicing.d.mts.map +1 -1
  30. package/dist/array/impl/array-utils-slicing.mjs +3 -3
  31. package/dist/array/impl/array-utils-slicing.mjs.map +1 -1
  32. package/dist/array/impl/array-utils-transformation.d.mts +25 -25
  33. package/dist/array/impl/array-utils-transformation.d.mts.map +1 -1
  34. package/dist/array/impl/array-utils-transformation.mjs +22 -22
  35. package/dist/array/impl/array-utils-transformation.mjs.map +1 -1
  36. package/dist/array/impl/array-utils-validation.d.mts.map +1 -1
  37. package/dist/array/impl/array-utils-validation.mjs +2 -2
  38. package/dist/array/impl/array-utils-validation.mjs.map +1 -1
  39. package/dist/collections/imap-mapped.d.mts +2 -2
  40. package/dist/collections/imap.d.mts +3 -4
  41. package/dist/collections/imap.d.mts.map +1 -1
  42. package/dist/collections/imap.mjs +0 -1
  43. package/dist/collections/imap.mjs.map +1 -1
  44. package/dist/collections/iset-mapped.d.mts +1 -2
  45. package/dist/collections/iset-mapped.d.mts.map +1 -1
  46. package/dist/collections/iset-mapped.mjs.map +1 -1
  47. package/dist/collections/iset.d.mts +4 -5
  48. package/dist/collections/iset.d.mts.map +1 -1
  49. package/dist/collections/iset.mjs +0 -1
  50. package/dist/collections/iset.mjs.map +1 -1
  51. package/dist/number/num.d.mts +2 -2
  52. package/dist/number/num.mjs.map +1 -1
  53. package/dist/object/object.d.mts +2 -2
  54. package/dist/others/fast-deep-equal.d.mts.map +1 -1
  55. package/dist/others/fast-deep-equal.mjs +10 -7
  56. package/dist/others/fast-deep-equal.mjs.map +1 -1
  57. package/dist/others/index.d.mts +1 -0
  58. package/dist/others/index.d.mts.map +1 -1
  59. package/dist/others/index.mjs +1 -0
  60. package/dist/others/index.mjs.map +1 -1
  61. package/dist/others/replace-equal-deep.d.mts +95 -0
  62. package/dist/others/replace-equal-deep.d.mts.map +1 -0
  63. package/dist/others/replace-equal-deep.mjs +156 -0
  64. package/dist/others/replace-equal-deep.mjs.map +1 -0
  65. package/dist/string/str.d.mts +2 -2
  66. package/package.json +12 -12
  67. package/src/array/impl/array-utils-element-access.mts +11 -11
  68. package/src/array/impl/array-utils-length-bounded-array-cast.mts +69 -69
  69. package/src/array/impl/array-utils-length-bounded-array-guard.mts +21 -21
  70. package/src/array/impl/array-utils-length-bounded-array-guard.test.mts +2 -2
  71. package/src/array/impl/array-utils-length-bounded-tuple-cast.mts +64 -65
  72. package/src/array/impl/array-utils-length-bounded-tuple-guard.mts +15 -15
  73. package/src/array/impl/array-utils-modification.mts +54 -54
  74. package/src/array/impl/array-utils-reducing-value.mts +55 -55
  75. package/src/array/impl/array-utils-search.mts +114 -114
  76. package/src/array/impl/array-utils-slicing.mts +16 -16
  77. package/src/array/impl/array-utils-transformation.mts +96 -96
  78. package/src/array/impl/array-utils-transformation.test.mts +2 -2
  79. package/src/array/impl/array-utils-validation.mts +19 -19
  80. package/src/array/impl/array-utils-validation.test.mts +1 -1
  81. package/src/collections/imap-mapped.mts +2 -2
  82. package/src/collections/imap.mts +3 -4
  83. package/src/collections/imap.test.mts +1 -1
  84. package/src/collections/iset-mapped.mts +1 -2
  85. package/src/collections/iset.mts +4 -5
  86. package/src/collections/iset.test.mts +1 -1
  87. package/src/number/num.mts +10 -10
  88. package/src/object/object.mts +2 -2
  89. package/src/others/fast-deep-equal.mts +17 -10
  90. package/src/others/fast-deep-equal.test.mts +141 -29
  91. package/src/others/index.mts +1 -0
  92. package/src/others/replace-equal-deep.mts +225 -0
  93. package/src/others/replace-equal-deep.test.mts +348 -0
  94. package/src/string/str.mts +2 -2
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Returns `next` with every part deeply equal to the same part of `current`
3
+ * replaced by that part of `current` (structural sharing). When the whole of
4
+ * `next` is deeply equal to `current`, `current` itself is returned, so an
5
+ * `Object.is` comparison of the result with `current` tells whether anything
6
+ * changed, and a reader that compares by reference sees a new object only where
7
+ * the value really changed.
8
+ *
9
+ * Only plain objects and arrays are walked into. Anything else — a `Map`, a
10
+ * `Set`, a `Date`, a class instance — is compared whole with
11
+ * {@link fastDeepEqual} and kept or replaced whole.
12
+ *
13
+ * Array items are matched by index. Give `key` to match them by key instead:
14
+ * an item for which it returns something other than `undefined` is matched with
15
+ * the item of `current` it returns the same key for, and an item it returns
16
+ * `undefined` for is still matched by index. The one `key` is used for the
17
+ * arrays at every depth.
18
+ *
19
+ * Neither argument is changed. The result shares objects with both, so it
20
+ * relies on neither being changed afterwards.
21
+ *
22
+ * Call it where the new value is written over the one held, so that nothing
23
+ * downstream has a new object it must compare again.
24
+ *
25
+ * @param current - The value held now
26
+ * @param next - The value to write over it
27
+ * @param options - {@link ReplaceEqualDeepOptions}
28
+ * @returns `current` when `next` is deeply equal to it; otherwise a value
29
+ * deeply equal to `next` that reuses the equal parts of `current`
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * type PullReport = DeepReadonly<{
34
+ * summary: { open: number };
35
+ * pulls: { number: number; title: string }[];
36
+ * }>;
37
+ *
38
+ * const held: PullReport = {
39
+ * summary: { open: 2 },
40
+ * pulls: [
41
+ * { number: 1, title: 'one' },
42
+ * { number: 2, title: 'two' },
43
+ * ],
44
+ * } as const;
45
+ *
46
+ * // A server answers with the whole report every time.
47
+ * const answer: PullReport = {
48
+ * summary: { open: 2 },
49
+ * pulls: [
50
+ * { number: 1, title: 'one' },
51
+ * { number: 2, title: 'two (edited)' },
52
+ * ],
53
+ * } as const;
54
+ *
55
+ * const written = replaceEqualDeep(held, answer);
56
+ *
57
+ * assert.deepStrictEqual(written, answer);
58
+ *
59
+ * // What did not change is the object already held,
60
+ * assert.strictEqual(written.summary, held.summary);
61
+ *
62
+ * assert.strictEqual(written.pulls[0], held.pulls[0]);
63
+ *
64
+ * // and what changed is new.
65
+ * assert.notStrictEqual(written.pulls[1], held.pulls[1]);
66
+ *
67
+ * // An answer that changes nothing gives back the value held.
68
+ * assert.strictEqual(replaceEqualDeep(held, structuredClone(held)), held);
69
+ *
70
+ * // Match array items by key rather than by index.
71
+ * const opened = replaceEqualDeep(
72
+ * held.pulls,
73
+ * [{ number: 0, title: 'zero' }, ...structuredClone(held.pulls)],
74
+ * {
75
+ * key: (item) =>
76
+ * isRecord(item) && hasKey(item, 'number') ? item.number : undefined,
77
+ * },
78
+ * );
79
+ *
80
+ * assert.strictEqual(opened[1], held.pulls[0]);
81
+ *
82
+ * assert.strictEqual(opened[2], held.pulls[1]);
83
+ * ```
84
+ */
85
+ export declare const replaceEqualDeep: <T>(current: T, next: T, options?: ReplaceEqualDeepOptions) => T;
86
+ /** Options of {@link replaceEqualDeep}. */
87
+ export type ReplaceEqualDeepOptions = Readonly<{
88
+ /**
89
+ * The key an array item is matched by. Items it returns `undefined` for are
90
+ * matched by index. Keys are compared with `SameValueZero`, as `Map` compares
91
+ * them.
92
+ */
93
+ key?: (item: unknown) => unknown;
94
+ }>;
95
+ //# sourceMappingURL=replace-equal-deep.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace-equal-deep.d.mts","sourceRoot":"","sources":["../../src/others/replace-equal-deep.mts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmFG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,WACvB,CAAC,QACJ,CAAC,YACG,uBAAuB,KAChC,CAIwC,CAAC;AAE5C,2CAA2C;AAC3C,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC7C;;;;OAIG;IACH,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;CAClC,CAAC,CAAC"}
@@ -0,0 +1,156 @@
1
+ import { hasKey, isRecord } from '../guard/index.mjs';
2
+ import { fastDeepEqual } from './fast-deep-equal.mjs';
3
+
4
+
5
+
6
+
7
+
8
+
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
19
+
20
+
21
+
22
+
23
+
24
+
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+
86
+
87
+ export const replaceEqualDeep = (current, next, options) =>
88
+
89
+
90
+
91
+ replace(current, next, options?.key);
92
+ const isPlainArray = (value) => Array.isArray(value) && Object.getPrototypeOf(value) === Array.prototype;
93
+ const isPlainObject = (value) => isRecord(value) && Object.getPrototypeOf(value) === Object.prototype;
94
+ const replace = (current, next, key) => Object.is(current, next)
95
+ ? current
96
+ : isPlainArray(current) && isPlainArray(next)
97
+ ? replaceArray(current, next, key)
98
+ : isPlainObject(current) && isPlainObject(next)
99
+ ? replaceObject(current, next, key)
100
+ : fastDeepEqual(current, next)
101
+ ? current
102
+ : next;
103
+ const indexByKey = (items, key) => {
104
+ const mut_byKey = new Map();
105
+ for (const item of items) {
106
+ const itemKey = key(item);
107
+ if (itemKey !== undefined && !mut_byKey.has(itemKey)) {
108
+ mut_byKey.set(itemKey, item);
109
+ }
110
+ }
111
+ return mut_byKey;
112
+ };
113
+
114
+
115
+
116
+ const replaceArray = (current, next, key) => {
117
+ const byKey = key === undefined ? undefined : indexByKey(current, key);
118
+ let mut_isCurrent = current.length === next.length;
119
+ let mut_isNext = true;
120
+ const mut_shared = [];
121
+ for (let mut_index = 0; mut_index < next.length; mut_index += 1) {
122
+ const index = mut_index;
123
+ const item = next[index];
124
+ const itemKey = key?.(item);
125
+ const before = itemKey === undefined || byKey === undefined
126
+ ? index < current.length
127
+ ? current[index]
128
+ : absent
129
+ : byKey.has(itemKey)
130
+ ? byKey.get(itemKey)
131
+ : absent;
132
+ const result = before === absent ? item : replace(before, item, key);
133
+ mut_isCurrent &&= Object.is(result, current[index]);
134
+ mut_isNext &&= Object.is(result, item);
135
+ mut_shared.push(result);
136
+ }
137
+ return mut_isCurrent ? current : mut_isNext ? next : mut_shared;
138
+ };
139
+ const replaceObject = (current, next, key) => {
140
+ const nextKeys = Object.keys(next);
141
+ let mut_isCurrent = Object.keys(current).length === nextKeys.length;
142
+ let mut_isNext = true;
143
+ const mut_shared = {};
144
+ for (const k of nextKeys) {
145
+ const value = next[k];
146
+ const has = hasKey(current, k);
147
+ const result = has ? replace(current[k], value, key) : value;
148
+ mut_isCurrent &&= has && Object.is(result, current[k]);
149
+ mut_isNext &&= Object.is(result, value);
150
+ mut_shared[k] = result;
151
+ }
152
+ return mut_isCurrent ? current : mut_isNext ? next : mut_shared;
153
+ };
154
+
155
+ const absent = Symbol('absent');
156
+ //# sourceMappingURL=replace-equal-deep.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replace-equal-deep.mjs","sourceRoot":"","sources":["../../src/others/replace-equal-deep.mts"],"names":[],"mappings":"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"}
@@ -321,8 +321,8 @@ export declare namespace Str {
321
321
  * `NonEmptyString` is an alias of `MinLengthString<1>`, so this is the
322
322
  * length-1 specialization of {@link asMinLengthString}. Use this instead of
323
323
  * writing `s as unknown as NonEmptyString`: the length is verified at
324
- * runtime, and the original string type (e.g. literal types) is preserved
325
- * via intersection.
324
+ * runtime, and the original string type (e.g. literal types) is preserved via
325
+ * intersection.
326
326
  *
327
327
  * @example
328
328
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-data-forge",
3
- "version": "14.9.0",
3
+ "version": "14.10.1",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript",
@@ -43,28 +43,28 @@
43
43
  "devDependencies": {
44
44
  "@emotion/react": "11.14.0",
45
45
  "@emotion/styled": "11.14.1",
46
- "@mui/material": "9.4.0",
46
+ "@mui/material": "9.5.0",
47
47
  "@types/react": "19.3.0",
48
- "@vitest/browser-playwright": "5.0.3",
49
- "eslint-config-typed": "5.19.0",
50
- "eslint-plugin-ts-fortress": "1.3.5",
51
- "eslint-plugin-ts-type-forge": "0.8.10",
52
- "immer": "11.1.21",
53
- "playwright": "1.64.0",
54
- "prettier": "3.9.9",
48
+ "@vitest/browser-playwright": "5.0.5",
49
+ "eslint-config-typed": "7.3.0",
50
+ "eslint-plugin-ts-fortress": "1.3.12",
51
+ "eslint-plugin-ts-type-forge": "0.8.17",
52
+ "immer": "11.2.7",
53
+ "playwright": "1.64.1",
54
+ "prettier": "3.9.10",
55
55
  "prettier-plugin-organize-imports": "4.3.0",
56
56
  "prettier-plugin-packagejson": "3.0.2",
57
- "react": "19.3.0",
57
+ "react": "19.3.1",
58
58
  "ts-repo-utils": "10.7.1",
59
59
  "typedoc": "0.28.20",
60
60
  "typedoc-github-theme": "0.4.0",
61
- "vite": "8.3.3"
61
+ "vite": "8.3.5"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "typescript": ">=4.8"
65
65
  },
66
66
  "engines": {
67
- "node": ">=22.22.2",
67
+ "node": ">=22.23.4",
68
68
  "pnpm": ">=9.0.0"
69
69
  },
70
70
  "publishConfig": {
@@ -53,17 +53,6 @@ export function at<E>(
53
53
  }
54
54
  }
55
55
 
56
- const atImpl = <E,>(
57
- array: readonly E[],
58
- index: SizeType.ArgArrWithNegative,
59
- ): Optional<E> =>
60
- pipe(index < 0 ? array.length + index : index).map((normalizedIndex) =>
61
- normalizedIndex < 0 || array.length <= normalizedIndex
62
- ? Optional.none
63
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
64
- Optional.some(array[normalizedIndex]!),
65
- ).value;
66
-
67
56
  /**
68
57
  * Returns the first element of an array as an Optional.
69
58
  *
@@ -126,6 +115,17 @@ export const last = <const Ar extends readonly unknown[]>(
126
115
  // eslint-disable-next-line total-functions/no-unsafe-type-assertion
127
116
  (array.length === 0 ? Optional.none : Optional.some(array.at(-1))) as never;
128
117
 
118
+ const atImpl = <E,>(
119
+ array: readonly E[],
120
+ index: SizeType.ArgArrWithNegative,
121
+ ): Optional<E> =>
122
+ pipe(index < 0 ? array.length + index : index).map((normalizedIndex) =>
123
+ normalizedIndex < 0 || array.length <= normalizedIndex
124
+ ? Optional.none
125
+ : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
126
+ Optional.some(array[normalizedIndex]!),
127
+ ).value;
128
+
129
129
  /**
130
130
  * Alias for `head`.
131
131
  *
@@ -18,8 +18,8 @@ import {
18
18
  * exactly `length` elements.
19
19
  *
20
20
  * Use this instead of writing `xs as unknown as FixedLengthArray<N, E>`: the
21
- * length is verified at runtime, and the original array type (e.g. tuple
22
- * types) is preserved via intersection, so the result also benefits from the
21
+ * length is verified at runtime, and the original array type (e.g. tuple types)
22
+ * is preserved via intersection, so the result also benefits from the
23
23
  * structural tuple prefix (in-range indexed access without `undefined` under
24
24
  * `noUncheckedIndexedAccess`).
25
25
  *
@@ -83,28 +83,13 @@ export function asFixedLengthArray<E>(
83
83
  }
84
84
  }
85
85
 
86
- const asFixedLengthArrayImpl = <E,>(
87
- length: SupportedLength,
88
- xs: readonly E[],
89
- ): readonly E[] => {
90
- const actualLength = xs.length;
91
-
92
- if (!isFixedLengthArray(length, xs)) {
93
- throw new TypeError(
94
- `Expected an array of length ${length}, got an array of length ${actualLength}`,
95
- );
96
- }
97
-
98
- return xs;
99
- };
100
-
101
86
  /**
102
87
  * Casts an array to `MinLengthArray<MinLength, E>` after checking that it has
103
88
  * at least `minLength` elements.
104
89
  *
105
90
  * Use this instead of writing `xs as unknown as MinLengthArray<N, E>`: the
106
- * length is verified at runtime, and the original array type (e.g. tuple
107
- * types) is preserved via intersection, so the result also benefits from the
91
+ * length is verified at runtime, and the original array type (e.g. tuple types)
92
+ * is preserved via intersection, so the result also benefits from the
108
93
  * structural tuple prefix (indexed access below `min(N, 10)` without
109
94
  * `undefined` under `noUncheckedIndexedAccess`).
110
95
  *
@@ -168,28 +153,13 @@ export function asMinLengthArray<E>(
168
153
  }
169
154
  }
170
155
 
171
- const asMinLengthArrayImpl = <E,>(
172
- minLength: SupportedLength,
173
- xs: readonly E[],
174
- ): readonly E[] => {
175
- const actualLength = xs.length;
176
-
177
- if (!isMinLengthArray(minLength, xs)) {
178
- throw new TypeError(
179
- `Expected an array of length >= ${minLength}, got an array of length ${actualLength}`,
180
- );
181
- }
182
-
183
- return xs;
184
- };
185
-
186
156
  /**
187
157
  * Casts an array to `MaxLengthArray<MaxLength, E>` after checking that it has
188
158
  * at most `maxLength` elements.
189
159
  *
190
160
  * Use this instead of writing `xs as unknown as MaxLengthArray<N, E>`: the
191
- * length is verified at runtime, and the original array type (e.g. tuple
192
- * types) is preserved via intersection.
161
+ * length is verified at runtime, and the original array type (e.g. tuple types)
162
+ * is preserved via intersection.
193
163
  *
194
164
  * The length argument comes first, matching the type-parameter order of
195
165
  * `MaxLengthArray<MaxLength, Elm>`. Omitting the array returns a function that
@@ -247,29 +217,14 @@ export function asMaxLengthArray<E>(
247
217
  }
248
218
  }
249
219
 
250
- const asMaxLengthArrayImpl = <E,>(
251
- maxLength: SupportedLength,
252
- xs: readonly E[],
253
- ): readonly E[] => {
254
- const actualLength = xs.length;
255
-
256
- if (!isMaxLengthArray(maxLength, xs)) {
257
- throw new TypeError(
258
- `Expected an array of length <= ${maxLength}, got an array of length ${actualLength}`,
259
- );
260
- }
261
-
262
- return xs;
263
- };
264
-
265
220
  /**
266
221
  * Casts an array to `BoundedLengthArray<MinLength, MaxLength, E>` after
267
222
  * checking that its length is within the inclusive range
268
223
  * `[minLength, maxLength]`.
269
224
  *
270
225
  * Use this instead of writing
271
- * `xs as unknown as BoundedLengthArray<Min, Max, E>`: the length is verified
272
- * at runtime, and the original array type (e.g. tuple types) is preserved via
226
+ * `xs as unknown as BoundedLengthArray<Min, Max, E>`: the length is verified at
227
+ * runtime, and the original array type (e.g. tuple types) is preserved via
273
228
  * intersection.
274
229
  *
275
230
  * The length arguments come first, matching the type-parameter order of
@@ -344,22 +299,6 @@ export function asBoundedLengthArray<E>(
344
299
  }
345
300
  }
346
301
 
347
- const asBoundedLengthArrayImpl = <E,>(
348
- minLength: SupportedLength,
349
- maxLength: SupportedLength,
350
- xs: readonly E[],
351
- ): readonly E[] => {
352
- const actualLength = xs.length;
353
-
354
- if (!isBoundedLengthArray(minLength, maxLength, xs)) {
355
- throw new TypeError(
356
- `Expected an array of length in [${minLength}, ${maxLength}], got an array of length ${actualLength}`,
357
- );
358
- }
359
-
360
- return xs;
361
- };
362
-
363
302
  /**
364
303
  * Casts an array to `FixedLengthArray<0, E>` after checking that it is empty.
365
304
  *
@@ -434,3 +373,64 @@ export const asNonEmptyArray = <Xs extends readonly unknown[]>(
434
373
 
435
374
  return xs;
436
375
  };
376
+
377
+ const asFixedLengthArrayImpl = <E,>(
378
+ length: SupportedLength,
379
+ xs: readonly E[],
380
+ ): readonly E[] => {
381
+ const actualLength = xs.length;
382
+
383
+ if (!isFixedLengthArray(length, xs)) {
384
+ throw new TypeError(
385
+ `Expected an array of length ${length}, got an array of length ${actualLength}`,
386
+ );
387
+ }
388
+
389
+ return xs;
390
+ };
391
+
392
+ const asMinLengthArrayImpl = <E,>(
393
+ minLength: SupportedLength,
394
+ xs: readonly E[],
395
+ ): readonly E[] => {
396
+ const actualLength = xs.length;
397
+
398
+ if (!isMinLengthArray(minLength, xs)) {
399
+ throw new TypeError(
400
+ `Expected an array of length >= ${minLength}, got an array of length ${actualLength}`,
401
+ );
402
+ }
403
+
404
+ return xs;
405
+ };
406
+
407
+ const asMaxLengthArrayImpl = <E,>(
408
+ maxLength: SupportedLength,
409
+ xs: readonly E[],
410
+ ): readonly E[] => {
411
+ const actualLength = xs.length;
412
+
413
+ if (!isMaxLengthArray(maxLength, xs)) {
414
+ throw new TypeError(
415
+ `Expected an array of length <= ${maxLength}, got an array of length ${actualLength}`,
416
+ );
417
+ }
418
+
419
+ return xs;
420
+ };
421
+
422
+ const asBoundedLengthArrayImpl = <E,>(
423
+ minLength: SupportedLength,
424
+ maxLength: SupportedLength,
425
+ xs: readonly E[],
426
+ ): readonly E[] => {
427
+ const actualLength = xs.length;
428
+
429
+ if (!isBoundedLengthArray(minLength, maxLength, xs)) {
430
+ throw new TypeError(
431
+ `Expected an array of length in [${minLength}, ${maxLength}], got an array of length ${actualLength}`,
432
+ );
433
+ }
434
+
435
+ return xs;
436
+ };
@@ -85,11 +85,6 @@ export function isMinLengthArray(
85
85
  }
86
86
  }
87
87
 
88
- const hasMinLength = (
89
- minLength: SupportedLength,
90
- xs: readonly unknown[],
91
- ): boolean => xs.length >= minLength;
92
-
93
88
  /**
94
89
  * Type guard that checks if an array has at most `maxLength` elements.
95
90
  *
@@ -169,11 +164,6 @@ export function isMaxLengthArray(
169
164
  }
170
165
  }
171
166
 
172
- const hasMaxLength = (
173
- maxLength: SupportedLength,
174
- xs: readonly unknown[],
175
- ): boolean => xs.length <= maxLength;
176
-
177
167
  /**
178
168
  * Type guard that checks if an array's length is within the inclusive range
179
169
  * `[minLength, maxLength]`.
@@ -267,12 +257,6 @@ export function isBoundedLengthArray(
267
257
  }
268
258
  }
269
259
 
270
- const hasBoundedLength = (
271
- minLength: SupportedLength,
272
- maxLength: SupportedLength,
273
- xs: readonly unknown[],
274
- ): boolean => minLength <= xs.length && xs.length <= maxLength;
275
-
276
260
  /**
277
261
  * Type guard that checks if an array has exactly `length` elements.
278
262
  *
@@ -350,11 +334,6 @@ export function isFixedLengthArray(
350
334
  }
351
335
  }
352
336
 
353
- const hasFixedLength = (
354
- length: SupportedLength,
355
- xs: readonly unknown[],
356
- ): boolean => xs.length === length;
357
-
358
337
  /**
359
338
  * Type guard that checks if an array is empty.
360
339
  *
@@ -402,3 +381,24 @@ export const isEmpty = <Xs extends readonly unknown[]>(
402
381
  export const isNonEmpty = <Xs extends readonly unknown[]>(
403
382
  array: Xs,
404
383
  ): array is MinLengthArray<1, Xs[number]> & Xs => array.length > 0;
384
+
385
+ const hasMinLength = (
386
+ minLength: SupportedLength,
387
+ xs: readonly unknown[],
388
+ ): boolean => xs.length >= minLength;
389
+
390
+ const hasMaxLength = (
391
+ maxLength: SupportedLength,
392
+ xs: readonly unknown[],
393
+ ): boolean => xs.length <= maxLength;
394
+
395
+ const hasBoundedLength = (
396
+ minLength: SupportedLength,
397
+ maxLength: SupportedLength,
398
+ xs: readonly unknown[],
399
+ ): boolean => minLength <= xs.length && xs.length <= maxLength;
400
+
401
+ const hasFixedLength = (
402
+ length: SupportedLength,
403
+ xs: readonly unknown[],
404
+ ): boolean => xs.length === length;
@@ -166,8 +166,8 @@ describe(isFixedLengthArray, () => {
166
166
 
167
167
  expectType<typeof value, FixedLengthArray<4, number>>('!<=');
168
168
 
169
- // `length` is narrowed to the literal and in-range indexed access does
170
- // not include `undefined` (even under `noUncheckedIndexedAccess`)
169
+ // `length` is narrowed to the literal and in-range indexed access does not
170
+ // include `undefined` (even under `noUncheckedIndexedAccess`)
171
171
  const _len = value.length;
172
172
 
173
173
  expectType<typeof _len, 3>('=');