@evolu/common 7.4.0 → 8.0.0-next.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 +4 -61
- package/dist/src/Array.d.ts +343 -102
- package/dist/src/Array.d.ts.map +1 -1
- package/dist/src/Array.js +181 -85
- package/dist/src/Assert.d.ts +57 -11
- package/dist/src/Assert.d.ts.map +1 -1
- package/dist/src/Assert.js +47 -11
- package/dist/src/BigInt.d.ts +6 -1
- package/dist/src/BigInt.d.ts.map +1 -1
- package/dist/src/BigInt.js +5 -0
- package/dist/src/Brand.d.ts +40 -12
- package/dist/src/Brand.d.ts.map +1 -1
- package/dist/src/Brand.js +5 -0
- package/dist/src/Buffer.d.ts +6 -1
- package/dist/src/Buffer.d.ts.map +1 -1
- package/dist/src/Buffer.js +7 -2
- package/dist/src/Cache.d.ts +10 -2
- package/dist/src/Cache.d.ts.map +1 -1
- package/dist/src/Cache.js +6 -0
- package/dist/src/Callbacks.d.ts +10 -7
- package/dist/src/Callbacks.d.ts.map +1 -1
- package/dist/src/Callbacks.js +23 -13
- package/dist/src/Console.d.ts +361 -69
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +217 -117
- package/dist/src/Crypto.d.ts +70 -21
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +72 -27
- package/dist/src/Eq.d.ts +22 -2
- package/dist/src/Eq.d.ts.map +1 -1
- package/dist/src/Eq.js +21 -1
- package/dist/src/Error.d.ts +23 -12
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +27 -11
- package/dist/src/Function.d.ts +128 -56
- package/dist/src/Function.d.ts.map +1 -1
- package/dist/src/Function.js +118 -14
- package/dist/src/Identicon.d.ts +5 -0
- package/dist/src/Identicon.d.ts.map +1 -1
- package/dist/src/Identicon.js +6 -1
- package/dist/src/Lookup.d.ts +160 -0
- package/dist/src/Lookup.d.ts.map +1 -0
- package/dist/src/Lookup.js +192 -0
- package/dist/src/Microtask.d.ts +21 -0
- package/dist/src/Microtask.d.ts.map +1 -0
- package/dist/src/Microtask.js +37 -0
- package/dist/src/Number.d.ts +18 -4
- package/dist/src/Number.d.ts.map +1 -1
- package/dist/src/Number.js +23 -1
- package/dist/src/Object.d.ts +110 -5
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +108 -5
- package/dist/src/Option.d.ts +58 -0
- package/dist/src/Option.d.ts.map +1 -0
- package/dist/src/Option.js +43 -0
- package/dist/src/Order.d.ts +5 -0
- package/dist/src/Order.d.ts.map +1 -1
- package/dist/src/Order.js +8 -3
- package/dist/src/Platform.d.ts +36 -2
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +10 -6
- package/dist/src/Polyfills.d.ts +27 -0
- package/dist/src/Polyfills.d.ts.map +1 -0
- package/dist/src/Polyfills.js +299 -0
- package/dist/src/Random.d.ts +18 -7
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +9 -7
- package/dist/src/Redacted.d.ts +7 -4
- package/dist/src/Redacted.d.ts.map +1 -1
- package/dist/src/Redacted.js +5 -0
- package/dist/src/Ref.d.ts +31 -16
- package/dist/src/Ref.d.ts.map +1 -1
- package/dist/src/Ref.js +35 -7
- package/dist/src/RefCount.d.ts +62 -0
- package/dist/src/RefCount.d.ts.map +1 -0
- package/dist/src/RefCount.js +83 -0
- package/dist/src/Relation.d.ts +84 -29
- package/dist/src/Relation.d.ts.map +1 -1
- package/dist/src/Relation.js +83 -105
- package/dist/src/Resource.d.ts +263 -0
- package/dist/src/Resource.d.ts.map +1 -0
- package/dist/src/Resource.js +389 -0
- package/dist/src/Result.d.ts +390 -374
- package/dist/src/Result.d.ts.map +1 -1
- package/dist/src/Result.js +142 -70
- package/dist/src/Schedule.d.ts +953 -0
- package/dist/src/Schedule.d.ts.map +1 -0
- package/dist/src/Schedule.js +1199 -0
- package/dist/src/Set.d.ts +181 -0
- package/dist/src/Set.d.ts.map +1 -0
- package/dist/src/Set.js +137 -0
- package/dist/src/Skiplist.d.ts +6 -1
- package/dist/src/Skiplist.d.ts.map +1 -1
- package/dist/src/Skiplist.js +5 -0
- package/dist/src/Sqlite.d.ts +160 -60
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +290 -190
- package/dist/src/Store.d.ts +36 -28
- package/dist/src/Store.d.ts.map +1 -1
- package/dist/src/Store.js +55 -15
- package/dist/src/String.d.ts +5 -0
- package/dist/src/String.d.ts.map +1 -1
- package/dist/src/String.js +5 -0
- package/dist/src/Task.d.ts +2263 -423
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +1488 -348
- package/dist/src/Test.d.ts +122 -0
- package/dist/src/Test.d.ts.map +1 -0
- package/dist/src/Test.js +66 -0
- package/dist/src/Time.d.ts +201 -48
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +190 -86
- package/dist/src/Tracer.d.ts +48 -0
- package/dist/src/Tracer.d.ts.map +1 -0
- package/dist/src/Tracer.js +6 -0
- package/dist/src/Type.d.ts +582 -209
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +362 -240
- package/dist/src/Types.d.ts +160 -6
- package/dist/src/Types.d.ts.map +1 -1
- package/dist/src/Types.js +22 -2
- package/dist/src/WebSocket.d.ts +113 -58
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +210 -118
- package/dist/src/Worker.d.ts +245 -60
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +220 -64
- package/dist/src/index.d.ts +25 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +19 -3
- package/dist/src/local-first/Db.d.ts +24 -213
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +622 -293
- package/dist/src/local-first/Error.d.ts +12 -0
- package/dist/src/local-first/Error.d.ts.map +1 -0
- package/dist/src/local-first/Error.js +6 -0
- package/dist/src/local-first/Evolu.d.ts +330 -285
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +472 -402
- package/dist/src/local-first/LocalAuth.d.ts +10 -10
- package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
- package/dist/src/local-first/LocalAuth.js +6 -1
- package/dist/src/local-first/Owner.d.ts +70 -27
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +27 -14
- package/dist/src/local-first/Protocol.d.ts +58 -50
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +253 -155
- package/dist/src/local-first/Query.d.ts +133 -43
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Query.js +139 -90
- package/dist/src/local-first/Relay.d.ts +18 -38
- package/dist/src/local-first/Relay.d.ts.map +1 -1
- package/dist/src/local-first/Relay.js +59 -171
- package/dist/src/local-first/Schema.d.ts +139 -144
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +78 -163
- package/dist/src/local-first/Shared.d.ts +125 -0
- package/dist/src/local-first/Shared.d.ts.map +1 -0
- package/dist/src/local-first/Shared.js +569 -0
- package/dist/src/local-first/Storage.d.ts +50 -53
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +121 -173
- package/dist/src/local-first/Timestamp.d.ts +28 -35
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +24 -25
- package/dist/src/local-first/index.d.ts +2 -15
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +2 -15
- package/package.json +36 -26
- package/src/Array.ts +467 -106
- package/src/Assert.ts +87 -12
- package/src/BigInt.ts +7 -1
- package/src/Brand.ts +41 -12
- package/src/Buffer.ts +10 -4
- package/src/Cache.ts +11 -2
- package/src/Callbacks.ts +33 -23
- package/src/Console.ts +567 -178
- package/src/Crypto.ts +111 -62
- package/src/Eq.ts +30 -3
- package/src/Error.ts +50 -34
- package/src/Function.ts +140 -68
- package/src/Identicon.ts +7 -1
- package/src/Lookup.ts +415 -0
- package/src/Microtask.ts +58 -0
- package/src/Number.ts +40 -7
- package/src/Object.ts +155 -16
- package/src/Option.ts +74 -0
- package/src/Order.ts +11 -3
- package/src/Platform.ts +43 -7
- package/src/Polyfills.ts +465 -0
- package/src/Random.ts +25 -11
- package/src/Redacted.ts +8 -4
- package/src/Ref.ts +72 -21
- package/src/RefCount.ts +170 -0
- package/src/Relation.ts +188 -136
- package/src/Resource.ts +864 -0
- package/src/Result.ts +548 -381
- package/src/Schedule.ts +1471 -0
- package/src/Set.ts +247 -0
- package/src/Skiplist.ts +7 -1
- package/src/Sqlite.ts +409 -304
- package/src/Store.ts +94 -48
- package/src/String.ts +6 -0
- package/src/Task.ts +3685 -740
- package/src/Test.ts +162 -0
- package/src/Time.ts +348 -145
- package/src/Tracer.ts +54 -0
- package/src/Type.ts +1064 -630
- package/src/Types.ts +202 -7
- package/src/WebSocket.ts +311 -178
- package/src/Worker.ts +527 -147
- package/src/index.ts +74 -3
- package/src/local-first/Db.ts +871 -597
- package/src/local-first/Error.ts +17 -0
- package/src/local-first/Evolu.ts +884 -823
- package/src/local-first/LocalAuth.ts +10 -10
- package/src/local-first/Owner.ts +94 -42
- package/src/local-first/Protocol.ts +440 -303
- package/src/local-first/Query.ts +243 -194
- package/src/local-first/Relay.ts +91 -252
- package/src/local-first/Schema.ts +284 -394
- package/src/local-first/Shared.ts +932 -0
- package/src/local-first/Storage.ts +209 -282
- package/src/local-first/Timestamp.ts +38 -45
- package/src/local-first/index.ts +2 -16
- package/LICENSE +0 -21
- package/dist/src/Instances.d.ts +0 -34
- package/dist/src/Instances.d.ts.map +0 -1
- package/dist/src/Instances.js +0 -44
- package/dist/src/Resources.d.ts +0 -118
- package/dist/src/Resources.d.ts.map +0 -1
- package/dist/src/Resources.js +0 -197
- package/dist/src/local-first/Platform.d.ts +0 -25
- package/dist/src/local-first/Platform.d.ts.map +0 -1
- package/dist/src/local-first/Platform.js +0 -1
- package/dist/src/local-first/Public.d.ts +0 -22
- package/dist/src/local-first/Public.d.ts.map +0 -1
- package/dist/src/local-first/Public.js +0 -15
- package/dist/src/local-first/PublicKysely.d.ts +0 -148
- package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
- package/dist/src/local-first/PublicKysely.js +0 -184
- package/dist/src/local-first/Sync.d.ts +0 -112
- package/dist/src/local-first/Sync.d.ts.map +0 -1
- package/dist/src/local-first/Sync.js +0 -529
- package/src/Instances.ts +0 -90
- package/src/Resources.ts +0 -367
- package/src/local-first/Platform.ts +0 -27
- package/src/local-first/Public.ts +0 -42
- package/src/local-first/PublicKysely.ts +0 -239
- package/src/local-first/Sync.ts +0 -960
package/src/Array.ts
CHANGED
|
@@ -1,8 +1,67 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Array
|
|
3
|
-
* mutations
|
|
2
|
+
* Array helpers that help TypeScript help you.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
4
|
+
* - **Non-empty arrays**: compile-time guarantee of at least one element
|
|
5
|
+
* - **Readonly arrays**: prevents accidental mutation
|
|
6
|
+
*
|
|
7
|
+
* Instead of checking array length at runtime, use {@link NonEmptyReadonlyArray}
|
|
8
|
+
* so TypeScript rejects empty arrays at compile time. Functions like
|
|
9
|
+
* {@link firstInArray} require a non-empty array — TypeScript won't let us pass
|
|
10
|
+
* an empty one. {@link mapArray} preserves non-emptiness (native `map` doesn't),
|
|
11
|
+
* while {@link appendToArray} and {@link prependToArray} guarantee the result is
|
|
12
|
+
* non-empty.
|
|
13
|
+
*
|
|
14
|
+
* All helpers return readonly arrays for safety. Consider how dangerous native
|
|
15
|
+
* `sort()` is — it mutates the original array and returns it, making bugs hard
|
|
16
|
+
* to track:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* const sortScores = (arr: number[]) => arr.sort((a, b) => a - b);
|
|
20
|
+
*
|
|
21
|
+
* const scores = [3, 1, 2];
|
|
22
|
+
* const leaderboard = sortScores(scores);
|
|
23
|
+
* leaderboard; // [1, 2, 3]
|
|
24
|
+
* scores; // [1, 2, 3] — original order lost!
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Imagine every method doing that.
|
|
28
|
+
*
|
|
29
|
+
* On a `ReadonlyArray`, `.sort()` doesn't even exist. Use {@link sortArray}
|
|
30
|
+
* instead:
|
|
31
|
+
*
|
|
32
|
+
* ```ts
|
|
33
|
+
* const sortScores = (arr: ReadonlyArray<number>) =>
|
|
34
|
+
* sortArray(arr, (a, b) => a - b);
|
|
35
|
+
*
|
|
36
|
+
* const scores: ReadonlyArray<number> = [3, 1, 2];
|
|
37
|
+
* const leaderboard = sortScores(scores);
|
|
38
|
+
* leaderboard; // [1, 2, 3]
|
|
39
|
+
* scores; // [3, 1, 2] — safe!
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* Even better, require a {@link NonEmptyReadonlyArray} — there's nothing to sort
|
|
43
|
+
* if the array is empty anyway:
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* const sortScores = (arr: NonEmptyReadonlyArray<number>) =>
|
|
47
|
+
* sortArray(arr, (a, b) => a - b);
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* Sorting an empty array isn't expensive, but functions can have side effects
|
|
51
|
+
* like database queries or network requests. Using non-empty arrays whenever
|
|
52
|
+
* possible is a good convention.
|
|
53
|
+
*
|
|
54
|
+
* For performance-critical cases where mutation is needed, Evolu provides
|
|
55
|
+
* {@link shiftFromArray} and {@link popFromArray} — but only because they improve
|
|
56
|
+
* type safety by returning a guaranteed `T` rather than an optional value.
|
|
57
|
+
*
|
|
58
|
+
* ### When to use native methods
|
|
59
|
+
*
|
|
60
|
+
* These helpers only exist where they add type-level value. Native methods like
|
|
61
|
+
* `find`, `some`, `every`, `includes`, `indexOf`, and `findIndex` work well on
|
|
62
|
+
* readonly arrays without mutation — use them directly.
|
|
63
|
+
*
|
|
64
|
+
* ## Examples
|
|
6
65
|
*
|
|
7
66
|
* ```ts
|
|
8
67
|
* // Types - compile-time guarantee of at least one element
|
|
@@ -12,15 +71,13 @@
|
|
|
12
71
|
*
|
|
13
72
|
* // Type guards
|
|
14
73
|
* const arr: ReadonlyArray<number> = [1, 2, 3];
|
|
15
|
-
* if (
|
|
74
|
+
* if (isNonEmptyArray(arr)) {
|
|
16
75
|
* firstInArray(arr);
|
|
17
76
|
* }
|
|
18
77
|
*
|
|
19
|
-
* //
|
|
78
|
+
* // Transformations
|
|
20
79
|
* const appended = appendToArray([1, 2, 3], 4); // [1, 2, 3, 4]
|
|
21
80
|
* const prepended = prependToArray([2, 3], 1); // [1, 2, 3]
|
|
22
|
-
*
|
|
23
|
-
* // Transformations
|
|
24
81
|
* const readonly: ReadonlyArray<number> = [1, 2, 3];
|
|
25
82
|
* const mapped = mapArray(readonly, (x) => x * 2); // [2, 4, 6]
|
|
26
83
|
* const filtered = filterArray(readonly, (x) => x > 1); // [2, 3]
|
|
@@ -28,7 +85,7 @@
|
|
|
28
85
|
* const [evens, odds] = partitionArray(
|
|
29
86
|
* [1, 2, 3, 4, 5],
|
|
30
87
|
* (x) => x % 2 === 0,
|
|
31
|
-
* );
|
|
88
|
+
* );
|
|
32
89
|
*
|
|
33
90
|
* // Accessors
|
|
34
91
|
* const first = firstInArray(["a", "b", "c"]); // "a"
|
|
@@ -36,121 +93,186 @@
|
|
|
36
93
|
*
|
|
37
94
|
* // Mutations
|
|
38
95
|
* const mutable: NonEmptyArray<number> = [1, 2, 3];
|
|
39
|
-
*
|
|
96
|
+
* shiftFromArray(mutable); // 1 (guaranteed to exist)
|
|
40
97
|
* mutable; // [2, 3]
|
|
41
98
|
* ```
|
|
42
99
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
100
|
+
* ## Composition
|
|
101
|
+
*
|
|
102
|
+
* All array helpers use a data-first style (the array is the first argument)
|
|
103
|
+
* because it's natural for single operations:
|
|
45
104
|
*
|
|
46
105
|
* ```ts
|
|
47
|
-
* // Data-first is natural for single operations.
|
|
48
106
|
* const timestamps = mapArray(messages, (m) => m.timestamp);
|
|
107
|
+
* ```
|
|
49
108
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* mapArray(dedupeArray(appendToArray(value, 2)), (x) => x * 2),
|
|
53
|
-
* );
|
|
109
|
+
* Data-first style also reads well for a few operations, often fitting on a
|
|
110
|
+
* line:
|
|
54
111
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* // |> mapArray(%, (x) => x * 2)
|
|
60
|
-
* // |> firstInArray(%);
|
|
61
|
-
*
|
|
62
|
-
* // Until the pipe operator lands, use nested calls or name each step:
|
|
63
|
-
* const appended = appendToArray(value, 2);
|
|
64
|
-
* const deduped = dedupeArray(appended);
|
|
65
|
-
* const mapped = mapArray(deduped, (x) => x * 2);
|
|
66
|
-
* const result = firstInArray(mapped);
|
|
112
|
+
* ```ts
|
|
113
|
+
* const cheapest = firstInArray(sortArray(prices, orderNumber));
|
|
114
|
+
* const uniqueNames = dedupeArray(mapArray(users, (u) => u.name));
|
|
115
|
+
* const latestDone = lastInArray(filterArray(jobs, isCompletedJob));
|
|
67
116
|
* ```
|
|
68
117
|
*
|
|
69
|
-
*
|
|
118
|
+
* For more operations, create a function like `getOldestActiveUser` or a
|
|
119
|
+
* generic helper.
|
|
70
120
|
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
121
|
+
* Some libraries provide dual APIs with data-last for pipe-based composition.
|
|
122
|
+
* Evolu prefers simplicity (in Latin, simplex means "one") so we don't have to
|
|
123
|
+
* choose between seemingly equivalent options (Buridan's ass dilemma).
|
|
74
124
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* - It aligns with the upcoming JavaScript pipe operator.
|
|
78
|
-
*
|
|
79
|
-
* **Note**: Feel free to use Array instance methods (mutation) if you think
|
|
80
|
-
* it's better (performance, local scope, etc.).
|
|
125
|
+
* Evolu doesn't provide `pipe` because few operations compose well without it,
|
|
126
|
+
* and for more operations, well-named functions communicate intent better.
|
|
81
127
|
*
|
|
82
128
|
* @module
|
|
83
129
|
*/
|
|
84
130
|
|
|
85
|
-
import {
|
|
131
|
+
import { identity } from "./Function.js";
|
|
132
|
+
import type { PredicateWithIndex, RefinementWithIndex } from "./Types.js";
|
|
86
133
|
|
|
87
134
|
/**
|
|
88
135
|
* An array with at least one element.
|
|
89
136
|
*
|
|
90
|
-
* @
|
|
137
|
+
* @group Types
|
|
91
138
|
*/
|
|
92
139
|
export type NonEmptyArray<T> = [T, ...Array<T>];
|
|
93
140
|
|
|
94
141
|
/**
|
|
95
142
|
* A readonly array with at least one element.
|
|
96
143
|
*
|
|
97
|
-
* @
|
|
144
|
+
* @group Types
|
|
98
145
|
*/
|
|
99
146
|
export type NonEmptyReadonlyArray<T> = readonly [T, ...ReadonlyArray<T>];
|
|
100
147
|
|
|
101
148
|
/**
|
|
102
|
-
*
|
|
149
|
+
* An empty readonly array.
|
|
150
|
+
*
|
|
151
|
+
* Use as a default or initial value to avoid allocating new empty arrays.
|
|
152
|
+
*
|
|
153
|
+
* @group Constants
|
|
154
|
+
*/
|
|
155
|
+
export const emptyArray: ReadonlyArray<never> = [];
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Better `Array.from`.
|
|
103
159
|
*
|
|
104
|
-
*
|
|
160
|
+
* - Returns readonly arrays
|
|
161
|
+
* - Accepts length directly: `arrayFrom(3, fn)` instead of `Array.from({ length:
|
|
162
|
+
* 3 }, fn)`
|
|
163
|
+
* - Skips copying if iterable is already an array (safe because readonly)
|
|
105
164
|
*
|
|
106
165
|
* ### Example
|
|
107
166
|
*
|
|
108
167
|
* ```ts
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
* }
|
|
168
|
+
* arrayFrom(new Set([1, 2, 3])); // ReadonlyArray<number>
|
|
169
|
+
* arrayFrom(3, (i) => i * 10); // [0, 10, 20]
|
|
170
|
+
* arrayFrom(iterableMaybeArray); // no unnecessary copy
|
|
113
171
|
* ```
|
|
114
172
|
*
|
|
115
|
-
*
|
|
173
|
+
* Unlike `Array.from`, there's no map parameter for iterables — use
|
|
174
|
+
* {@link mapArray} instead, or
|
|
175
|
+
* {@link https://web.dev/blog/baseline-iterator-helpers | iterator helpers}
|
|
176
|
+
* directly on iterables.
|
|
177
|
+
*
|
|
178
|
+
* @group Constructors
|
|
116
179
|
*/
|
|
117
|
-
export
|
|
118
|
-
|
|
119
|
-
|
|
180
|
+
export function arrayFrom<T>(iterable: Iterable<T>): ReadonlyArray<T>;
|
|
181
|
+
/** From length and map function. */
|
|
182
|
+
export function arrayFrom<T>(
|
|
183
|
+
length: number,
|
|
184
|
+
map: (index: number) => T,
|
|
185
|
+
): ReadonlyArray<T>;
|
|
186
|
+
export function arrayFrom<T>(
|
|
187
|
+
iterableOrLength: Iterable<T> | number,
|
|
188
|
+
map?: (index: number) => T,
|
|
189
|
+
): ReadonlyArray<T> {
|
|
190
|
+
if (typeof iterableOrLength === "number") {
|
|
191
|
+
return Array.from({ length: iterableOrLength }, (_, i) =>
|
|
192
|
+
(map as (index: number) => T)(i),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return Array.isArray(iterableOrLength)
|
|
196
|
+
? (iterableOrLength as ReadonlyArray<T>)
|
|
197
|
+
: [...iterableOrLength];
|
|
198
|
+
}
|
|
120
199
|
|
|
121
200
|
/**
|
|
122
|
-
*
|
|
123
|
-
* {@link NonEmptyReadonlyArray}.
|
|
201
|
+
* Better `Array.fromAsync`.
|
|
124
202
|
*
|
|
125
|
-
*
|
|
203
|
+
* Returns a readonly array and awaits promised items from sync or async
|
|
204
|
+
* iterables.
|
|
126
205
|
*
|
|
127
206
|
* ### Example
|
|
128
207
|
*
|
|
129
208
|
* ```ts
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
209
|
+
* await arrayFromAsync(new Set([1, 2, 3])); // ReadonlyArray<number>
|
|
210
|
+
* await arrayFromAsync(
|
|
211
|
+
* (async function* () {
|
|
212
|
+
* yield Promise.resolve(1);
|
|
213
|
+
* yield Promise.resolve(2);
|
|
214
|
+
* })(),
|
|
215
|
+
* ); // [1, 2]
|
|
216
|
+
* ```
|
|
217
|
+
*
|
|
218
|
+
* Unlike `Array.fromAsync`, there's no map parameter — map the result with
|
|
219
|
+
* {@link mapArray} or use
|
|
220
|
+
* {@link https://web.dev/blog/baseline-iterator-helpers | iterator helpers}
|
|
221
|
+
* directly on iterables.
|
|
222
|
+
*
|
|
223
|
+
* @group Constructors
|
|
224
|
+
*/
|
|
225
|
+
export const arrayFromAsync = async <T>(
|
|
226
|
+
iterable: AsyncIterable<T> | Iterable<T | PromiseLike<T>>,
|
|
227
|
+
): Promise<ReadonlyArray<T>> => Array.fromAsync(iterable);
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Checks if an array is non-empty and narrows its type to {@link NonEmptyArray}
|
|
231
|
+
* or {@link NonEmptyReadonlyArray} based on the input.
|
|
232
|
+
*
|
|
233
|
+
* To check if an array is empty, use `if (!isNonEmptyArray(arr))` — using the
|
|
234
|
+
* negated guard is better than `.length === 0` for early returns because
|
|
235
|
+
* TypeScript narrows the type after the check.
|
|
236
|
+
*
|
|
237
|
+
* ### Example
|
|
238
|
+
*
|
|
239
|
+
* ```ts
|
|
240
|
+
* // Mutable array narrows to NonEmptyArray
|
|
241
|
+
* const arr: Array<number> = [1, 2, 3];
|
|
242
|
+
* if (isNonEmptyArray(arr)) {
|
|
243
|
+
* shiftFromArray(arr); // arr is NonEmptyArray<number>
|
|
244
|
+
* }
|
|
245
|
+
*
|
|
246
|
+
* // Readonly array narrows to NonEmptyReadonlyArray
|
|
247
|
+
* const readonly: ReadonlyArray<number> = [1, 2, 3];
|
|
248
|
+
* if (isNonEmptyArray(readonly)) {
|
|
249
|
+
* firstInArray(readonly); // readonly is NonEmptyReadonlyArray<number>
|
|
133
250
|
* }
|
|
134
251
|
* ```
|
|
135
252
|
*
|
|
136
|
-
* @
|
|
253
|
+
* @group Types
|
|
137
254
|
*/
|
|
138
|
-
export
|
|
255
|
+
export function isNonEmptyArray<T>(array: Array<T>): array is NonEmptyArray<T>;
|
|
256
|
+
/** Readonly array overload. */
|
|
257
|
+
export function isNonEmptyArray<T>(
|
|
258
|
+
array: ReadonlyArray<T>,
|
|
259
|
+
): array is NonEmptyReadonlyArray<T>;
|
|
260
|
+
export function isNonEmptyArray<T>(
|
|
139
261
|
array: ReadonlyArray<T>,
|
|
140
|
-
): array is NonEmptyReadonlyArray<T>
|
|
262
|
+
): array is NonEmptyReadonlyArray<T> {
|
|
263
|
+
return array.length > 0;
|
|
264
|
+
}
|
|
141
265
|
|
|
142
266
|
/**
|
|
143
267
|
* Appends an item to an array, returning a new non-empty readonly array.
|
|
144
268
|
*
|
|
145
|
-
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
146
|
-
*
|
|
147
269
|
* ### Example
|
|
148
270
|
*
|
|
149
271
|
* ```ts
|
|
150
272
|
* appendToArray([1, 2, 3], 4); // [1, 2, 3, 4]
|
|
151
273
|
* ```
|
|
152
274
|
*
|
|
153
|
-
* @
|
|
275
|
+
* @group Transformations
|
|
154
276
|
*/
|
|
155
277
|
export const appendToArray = <T>(
|
|
156
278
|
array: ReadonlyArray<T>,
|
|
@@ -161,15 +283,13 @@ export const appendToArray = <T>(
|
|
|
161
283
|
/**
|
|
162
284
|
* Prepends an item to an array, returning a new non-empty readonly array.
|
|
163
285
|
*
|
|
164
|
-
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
165
|
-
*
|
|
166
286
|
* ### Example
|
|
167
287
|
*
|
|
168
288
|
* ```ts
|
|
169
289
|
* prependToArray([2, 3], 1); // [1, 2, 3]
|
|
170
290
|
* ```
|
|
171
291
|
*
|
|
172
|
-
* @
|
|
292
|
+
* @group Transformations
|
|
173
293
|
*/
|
|
174
294
|
export const prependToArray = <T>(
|
|
175
295
|
array: ReadonlyArray<T>,
|
|
@@ -177,9 +297,9 @@ export const prependToArray = <T>(
|
|
|
177
297
|
): NonEmptyReadonlyArray<T> => [item, ...array] as NonEmptyReadonlyArray<T>;
|
|
178
298
|
|
|
179
299
|
/**
|
|
180
|
-
* Maps an array using a mapper function.
|
|
300
|
+
* Maps an array using a mapper function, returning a new readonly array.
|
|
181
301
|
*
|
|
182
|
-
*
|
|
302
|
+
* Preserves non-empty type.
|
|
183
303
|
*
|
|
184
304
|
* ### Example
|
|
185
305
|
*
|
|
@@ -187,12 +307,13 @@ export const prependToArray = <T>(
|
|
|
187
307
|
* mapArray([1, 2, 3], (x) => x * 2); // [2, 4, 6]
|
|
188
308
|
* ```
|
|
189
309
|
*
|
|
190
|
-
* @
|
|
310
|
+
* @group Transformations
|
|
191
311
|
*/
|
|
192
312
|
export function mapArray<T, U>(
|
|
193
313
|
array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
|
|
194
314
|
mapper: (item: T, index: number) => U,
|
|
195
315
|
): NonEmptyReadonlyArray<U>;
|
|
316
|
+
/** Possibly empty array. */
|
|
196
317
|
export function mapArray<T, U>(
|
|
197
318
|
array: ReadonlyArray<T> | Array<T>,
|
|
198
319
|
mapper: (item: T, index: number) => U,
|
|
@@ -201,27 +322,134 @@ export function mapArray<T, U>(
|
|
|
201
322
|
array: ReadonlyArray<T> | Array<T>,
|
|
202
323
|
mapper: (item: T, index: number) => U,
|
|
203
324
|
): ReadonlyArray<U> {
|
|
204
|
-
|
|
325
|
+
// For loop is faster than array.map.
|
|
326
|
+
const length = array.length;
|
|
327
|
+
const result = new Array<U>(length);
|
|
328
|
+
for (let i = 0; i < length; i++) result[i] = mapper(array[i], i);
|
|
329
|
+
return result as ReadonlyArray<U>;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Maps each element to an array and flattens the result.
|
|
334
|
+
*
|
|
335
|
+
* Preserves non-empty type when the input is non-empty and the mapper returns
|
|
336
|
+
* non-empty arrays. When called without a mapper, flattens nested arrays using
|
|
337
|
+
* {@link identity}.
|
|
338
|
+
*
|
|
339
|
+
* ### Example
|
|
340
|
+
*
|
|
341
|
+
* ```ts
|
|
342
|
+
* flatMapArray([
|
|
343
|
+
* [1, 2],
|
|
344
|
+
* [3, 4],
|
|
345
|
+
* ]); // [1, 2, 3, 4]
|
|
346
|
+
*
|
|
347
|
+
* flatMapArray([1, 2, 3], (x) => [x, x * 10]); // [1, 10, 2, 20, 3, 30]
|
|
348
|
+
* ```
|
|
349
|
+
*
|
|
350
|
+
* ### Filter and map in one pass
|
|
351
|
+
*
|
|
352
|
+
* Return `[]` to filter out, `[value]` to keep:
|
|
353
|
+
*
|
|
354
|
+
* ```ts
|
|
355
|
+
* const errors = flatMapArray(fields, (f) => {
|
|
356
|
+
* const result = validate(f);
|
|
357
|
+
* return result.ok ? [] : [result.error];
|
|
358
|
+
* });
|
|
359
|
+
* ```
|
|
360
|
+
*
|
|
361
|
+
* @group Transformations
|
|
362
|
+
*/
|
|
363
|
+
export function flatMapArray<T>(
|
|
364
|
+
array:
|
|
365
|
+
| NonEmptyReadonlyArray<NonEmptyReadonlyArray<T> | NonEmptyArray<T>>
|
|
366
|
+
| NonEmptyArray<NonEmptyReadonlyArray<T> | NonEmptyArray<T>>,
|
|
367
|
+
): NonEmptyReadonlyArray<T>;
|
|
368
|
+
/** Possibly empty nested arrays. */
|
|
369
|
+
export function flatMapArray<T>(
|
|
370
|
+
array:
|
|
371
|
+
| ReadonlyArray<ReadonlyArray<T> | Array<T>>
|
|
372
|
+
| Array<ReadonlyArray<T> | Array<T>>,
|
|
373
|
+
): ReadonlyArray<T>;
|
|
374
|
+
/** Non-empty with mapper returning non-empty. */
|
|
375
|
+
export function flatMapArray<T, U>(
|
|
376
|
+
array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
|
|
377
|
+
mapper: (
|
|
378
|
+
item: T,
|
|
379
|
+
index: number,
|
|
380
|
+
) => NonEmptyReadonlyArray<U> | NonEmptyArray<U>,
|
|
381
|
+
): NonEmptyReadonlyArray<U>;
|
|
382
|
+
/** With mapper function. */
|
|
383
|
+
export function flatMapArray<T, U>(
|
|
384
|
+
array: ReadonlyArray<T> | Array<T>,
|
|
385
|
+
mapper: (item: T, index: number) => ReadonlyArray<U> | Array<U>,
|
|
386
|
+
): ReadonlyArray<U>;
|
|
387
|
+
export function flatMapArray<T, U>(
|
|
388
|
+
array: ReadonlyArray<T> | Array<T>,
|
|
389
|
+
mapper: (
|
|
390
|
+
item: T,
|
|
391
|
+
index: number,
|
|
392
|
+
) => ReadonlyArray<U> | Array<U> = identity as (
|
|
393
|
+
item: T,
|
|
394
|
+
index: number,
|
|
395
|
+
) => ReadonlyArray<U> | Array<U>,
|
|
396
|
+
): ReadonlyArray<U> {
|
|
397
|
+
return array.flatMap(mapper) as ReadonlyArray<U>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Concatenates two arrays, returning a new readonly array.
|
|
402
|
+
*
|
|
403
|
+
* Returns a non-empty array when at least one input is non-empty.
|
|
404
|
+
*
|
|
405
|
+
* ### Example
|
|
406
|
+
*
|
|
407
|
+
* ```ts
|
|
408
|
+
* concatArrays([1, 2], [3, 4]); // [1, 2, 3, 4]
|
|
409
|
+
* concatArrays([], [1]); // [1] (non-empty)
|
|
410
|
+
* concatArrays([1], []); // [1] (non-empty)
|
|
411
|
+
* ```
|
|
412
|
+
*
|
|
413
|
+
* @group Transformations
|
|
414
|
+
*/
|
|
415
|
+
export function concatArrays<T>(
|
|
416
|
+
first: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
|
|
417
|
+
second: ReadonlyArray<T> | Array<T>,
|
|
418
|
+
): NonEmptyReadonlyArray<T>;
|
|
419
|
+
/** Second non-empty. */
|
|
420
|
+
export function concatArrays<T>(
|
|
421
|
+
first: ReadonlyArray<T> | Array<T>,
|
|
422
|
+
second: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
|
|
423
|
+
): NonEmptyReadonlyArray<T>;
|
|
424
|
+
/** Both possibly empty. */
|
|
425
|
+
export function concatArrays<T>(
|
|
426
|
+
first: ReadonlyArray<T> | Array<T>,
|
|
427
|
+
second: ReadonlyArray<T> | Array<T>,
|
|
428
|
+
): ReadonlyArray<T>;
|
|
429
|
+
export function concatArrays<T>(
|
|
430
|
+
first: ReadonlyArray<T> | Array<T>,
|
|
431
|
+
second: ReadonlyArray<T> | Array<T>,
|
|
432
|
+
): ReadonlyArray<T> {
|
|
433
|
+
return [...first, ...second] as ReadonlyArray<T>;
|
|
205
434
|
}
|
|
206
435
|
|
|
207
436
|
/**
|
|
208
437
|
* Filters an array using a predicate or refinement function, returning a new
|
|
209
438
|
* readonly array.
|
|
210
439
|
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* like `PositiveInt.is`.
|
|
440
|
+
* When used with a refinement function (with `value is Type` syntax),
|
|
441
|
+
* TypeScript will narrow the result type to the narrowed type, making it useful
|
|
442
|
+
* for filtering with Evolu Types like `PositiveInt.is`.
|
|
215
443
|
*
|
|
216
|
-
* ###
|
|
444
|
+
* ### Example
|
|
217
445
|
*
|
|
218
|
-
*
|
|
446
|
+
* ### With predicate
|
|
219
447
|
*
|
|
220
448
|
* ```ts
|
|
221
449
|
* filterArray([1, 2, 3, 4, 5], (x) => x % 2 === 0); // [2, 4]
|
|
222
450
|
* ```
|
|
223
451
|
*
|
|
224
|
-
*
|
|
452
|
+
* ### With refinement
|
|
225
453
|
*
|
|
226
454
|
* ```ts
|
|
227
455
|
* const mixed: ReadonlyArray<NonEmptyString | PositiveInt> = [
|
|
@@ -232,12 +460,13 @@ export function mapArray<T, U>(
|
|
|
232
460
|
* // positiveInts: ReadonlyArray<PositiveInt> (narrowed type)
|
|
233
461
|
* ```
|
|
234
462
|
*
|
|
235
|
-
* @
|
|
463
|
+
* @group Transformations
|
|
236
464
|
*/
|
|
237
465
|
export function filterArray<T, S extends T>(
|
|
238
466
|
array: ReadonlyArray<T>,
|
|
239
467
|
refinement: RefinementWithIndex<T, S>,
|
|
240
468
|
): ReadonlyArray<S>;
|
|
469
|
+
/** With predicate. */
|
|
241
470
|
export function filterArray<T>(
|
|
242
471
|
array: ReadonlyArray<T>,
|
|
243
472
|
predicate: PredicateWithIndex<T>,
|
|
@@ -255,7 +484,6 @@ export function filterArray<T>(
|
|
|
255
484
|
* are used directly. Dedupes by reference equality of values (or extracted keys
|
|
256
485
|
* when `by` is used).
|
|
257
486
|
*
|
|
258
|
-
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
259
487
|
* Preserves non-empty type.
|
|
260
488
|
*
|
|
261
489
|
* ### Example
|
|
@@ -275,12 +503,13 @@ export function filterArray<T>(
|
|
|
275
503
|
* ); // [{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]
|
|
276
504
|
* ```
|
|
277
505
|
*
|
|
278
|
-
* @
|
|
506
|
+
* @group Transformations
|
|
279
507
|
*/
|
|
280
508
|
export function dedupeArray<T>(
|
|
281
509
|
array: NonEmptyReadonlyArray<T> | NonEmptyArray<T>,
|
|
282
510
|
by?: (item: T) => unknown,
|
|
283
511
|
): NonEmptyReadonlyArray<T>;
|
|
512
|
+
/** Possibly empty array. */
|
|
284
513
|
export function dedupeArray<T>(
|
|
285
514
|
array: ReadonlyArray<T> | Array<T>,
|
|
286
515
|
by?: (item: T) => unknown,
|
|
@@ -303,20 +532,19 @@ export function dedupeArray<T>(
|
|
|
303
532
|
}
|
|
304
533
|
|
|
305
534
|
/**
|
|
306
|
-
* Partitions an array into two arrays based on a predicate or
|
|
307
|
-
* function.
|
|
535
|
+
* Partitions an array into two readonly arrays based on a predicate or
|
|
536
|
+
* refinement function.
|
|
308
537
|
*
|
|
309
538
|
* Returns a tuple where the first array contains elements that satisfy the
|
|
310
|
-
* predicate, and the second array contains elements that do not.
|
|
311
|
-
* mutable and readonly arrays.
|
|
539
|
+
* predicate, and the second array contains elements that do not.
|
|
312
540
|
*
|
|
313
541
|
* When used with a refinement function (with `value is Type` syntax),
|
|
314
542
|
* TypeScript will narrow the first array to the narrowed type, making it useful
|
|
315
543
|
* for filtering with Evolu Types like `PositiveInt.is`.
|
|
316
544
|
*
|
|
317
|
-
* ###
|
|
545
|
+
* ### Example
|
|
318
546
|
*
|
|
319
|
-
*
|
|
547
|
+
* ### With predicate
|
|
320
548
|
*
|
|
321
549
|
* ```ts
|
|
322
550
|
* const [evens, odds] = partitionArray(
|
|
@@ -327,7 +555,7 @@ export function dedupeArray<T>(
|
|
|
327
555
|
* odds; // [1, 3, 5]
|
|
328
556
|
* ```
|
|
329
557
|
*
|
|
330
|
-
*
|
|
558
|
+
* ### With refinement
|
|
331
559
|
*
|
|
332
560
|
* ```ts
|
|
333
561
|
* const mixed: ReadonlyArray<NonEmptyString | PositiveInt> = [
|
|
@@ -339,12 +567,13 @@ export function dedupeArray<T>(
|
|
|
339
567
|
* // strings: ReadonlyArray<NonEmptyString> (Exclude<T, PositiveInt>)
|
|
340
568
|
* ```
|
|
341
569
|
*
|
|
342
|
-
* @
|
|
570
|
+
* @group Transformations
|
|
343
571
|
*/
|
|
344
572
|
export function partitionArray<T, S extends T>(
|
|
345
573
|
array: ReadonlyArray<T>,
|
|
346
574
|
refinement: RefinementWithIndex<T, S>,
|
|
347
575
|
): readonly [ReadonlyArray<S>, ReadonlyArray<Exclude<T, S>>];
|
|
576
|
+
/** With predicate. */
|
|
348
577
|
export function partitionArray<T>(
|
|
349
578
|
array: ReadonlyArray<T>,
|
|
350
579
|
predicate: PredicateWithIndex<T>,
|
|
@@ -368,9 +597,137 @@ export function partitionArray<T>(
|
|
|
368
597
|
}
|
|
369
598
|
|
|
370
599
|
/**
|
|
371
|
-
* Returns
|
|
600
|
+
* Returns a new sorted readonly array.
|
|
601
|
+
*
|
|
602
|
+
* Wraps native `toSorted`. Preserves non-empty type.
|
|
603
|
+
*
|
|
604
|
+
* ### Example
|
|
605
|
+
*
|
|
606
|
+
* ```ts
|
|
607
|
+
* sortArray([3, 1, 2], (a, b) => a - b); // [1, 2, 3]
|
|
608
|
+
* ```
|
|
609
|
+
*
|
|
610
|
+
* @group Transformations
|
|
611
|
+
*/
|
|
612
|
+
export function sortArray<T>(
|
|
613
|
+
array: NonEmptyReadonlyArray<T>,
|
|
614
|
+
compareFn?: (a: T, b: T) => number,
|
|
615
|
+
): NonEmptyReadonlyArray<T>;
|
|
616
|
+
/** Possibly empty array. */
|
|
617
|
+
export function sortArray<T>(
|
|
618
|
+
array: ReadonlyArray<T>,
|
|
619
|
+
compareFn?: (a: T, b: T) => number,
|
|
620
|
+
): ReadonlyArray<T>;
|
|
621
|
+
export function sortArray<T>(
|
|
622
|
+
array: ReadonlyArray<T>,
|
|
623
|
+
compareFn?: (a: T, b: T) => number,
|
|
624
|
+
): ReadonlyArray<T> {
|
|
625
|
+
return array.toSorted(compareFn) as ReadonlyArray<T>;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Returns a new reversed readonly array.
|
|
630
|
+
*
|
|
631
|
+
* Wraps native `toReversed`. Preserves non-empty type.
|
|
632
|
+
*
|
|
633
|
+
* ### Example
|
|
372
634
|
*
|
|
373
|
-
*
|
|
635
|
+
* ```ts
|
|
636
|
+
* reverseArray([1, 2, 3]); // [3, 2, 1]
|
|
637
|
+
* ```
|
|
638
|
+
*
|
|
639
|
+
* @group Transformations
|
|
640
|
+
*/
|
|
641
|
+
export function reverseArray<T>(
|
|
642
|
+
array: NonEmptyReadonlyArray<T>,
|
|
643
|
+
): NonEmptyReadonlyArray<T>;
|
|
644
|
+
/** Possibly empty array. */
|
|
645
|
+
export function reverseArray<T>(array: ReadonlyArray<T>): ReadonlyArray<T>;
|
|
646
|
+
export function reverseArray<T>(array: ReadonlyArray<T>): ReadonlyArray<T> {
|
|
647
|
+
return array.toReversed() as ReadonlyArray<T>;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Returns a new readonly array with elements removed and/or replaced.
|
|
652
|
+
*
|
|
653
|
+
* Wraps native `toSpliced`.
|
|
654
|
+
*
|
|
655
|
+
* ### Example
|
|
656
|
+
*
|
|
657
|
+
* ```ts
|
|
658
|
+
* spliceArray([1, 2, 3, 4], 1, 2); // [1, 4]
|
|
659
|
+
* spliceArray([1, 2, 3], 1, 1, 10, 11); // [1, 10, 11, 3]
|
|
660
|
+
* ```
|
|
661
|
+
*
|
|
662
|
+
* @group Transformations
|
|
663
|
+
*/
|
|
664
|
+
export const spliceArray = <T>(
|
|
665
|
+
array: ReadonlyArray<T>,
|
|
666
|
+
start: number,
|
|
667
|
+
deleteCount: number,
|
|
668
|
+
...items: ReadonlyArray<T>
|
|
669
|
+
): ReadonlyArray<T> => array.toSpliced(start, deleteCount, ...items);
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Extracts element types from a tuple of arrays, producing a tuple type.
|
|
673
|
+
*
|
|
674
|
+
* @group Types
|
|
675
|
+
*/
|
|
676
|
+
export type ZipArrayResult<T extends ReadonlyArray<ReadonlyArray<unknown>>> = {
|
|
677
|
+
[K in keyof T]: T[K] extends ReadonlyArray<infer U> ? U : never;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Combines multiple arrays into an array of tuples.
|
|
682
|
+
*
|
|
683
|
+
* Uses "shortest" mode — stops at the shortest input array. Preserves non-empty
|
|
684
|
+
* type when all input arrays are non-empty. See the
|
|
685
|
+
* {@link https://github.com/tc39/proposal-array-zip | TC39 Array.zip proposal}
|
|
686
|
+
* for the pattern this follows.
|
|
687
|
+
*
|
|
688
|
+
* ### Example
|
|
689
|
+
*
|
|
690
|
+
* ```ts
|
|
691
|
+
* zipArray([
|
|
692
|
+
* [1, 2, 3],
|
|
693
|
+
* ["a", "b", "c"],
|
|
694
|
+
* ]);
|
|
695
|
+
* // [[1, "a"], [2, "b"], [3, "c"]]
|
|
696
|
+
*
|
|
697
|
+
* zipArray([
|
|
698
|
+
* [1, 2],
|
|
699
|
+
* ["a", "b", "c"],
|
|
700
|
+
* [true, false],
|
|
701
|
+
* ]);
|
|
702
|
+
* // [[1, "a", true], [2, "b", false]]
|
|
703
|
+
* ```
|
|
704
|
+
*
|
|
705
|
+
* @group Transformations
|
|
706
|
+
*/
|
|
707
|
+
export function zipArray<
|
|
708
|
+
T extends NonEmptyReadonlyArray<NonEmptyReadonlyArray<unknown>>,
|
|
709
|
+
>(arrays: T): NonEmptyReadonlyArray<Readonly<ZipArrayResult<T>>>;
|
|
710
|
+
/** Possibly empty arrays. */
|
|
711
|
+
export function zipArray<T extends ReadonlyArray<ReadonlyArray<unknown>>>(
|
|
712
|
+
arrays: T,
|
|
713
|
+
): ReadonlyArray<Readonly<ZipArrayResult<T>>>;
|
|
714
|
+
export function zipArray<T extends ReadonlyArray<ReadonlyArray<unknown>>>(
|
|
715
|
+
arrays: T,
|
|
716
|
+
): ReadonlyArray<Readonly<ZipArrayResult<T>>> {
|
|
717
|
+
if (arrays.length === 0) return emptyArray;
|
|
718
|
+
|
|
719
|
+
const minLength = Math.min(...mapArray(arrays, (a) => a.length));
|
|
720
|
+
const result = new Array<unknown>(minLength);
|
|
721
|
+
|
|
722
|
+
for (let i = 0; i < minLength; i++) {
|
|
723
|
+
result[i] = mapArray(arrays, (a) => a[i]);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return result as ReadonlyArray<Readonly<ZipArrayResult<T>>>;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Returns the first element of a non-empty array.
|
|
374
731
|
*
|
|
375
732
|
* ### Example
|
|
376
733
|
*
|
|
@@ -378,56 +735,60 @@ export function partitionArray<T>(
|
|
|
378
735
|
* firstInArray(["a", "b", "c"]); // "a"
|
|
379
736
|
* ```
|
|
380
737
|
*
|
|
381
|
-
* @
|
|
738
|
+
* @group Accessors
|
|
382
739
|
*/
|
|
383
740
|
export const firstInArray = <T>(array: NonEmptyReadonlyArray<T>): T => array[0];
|
|
384
741
|
|
|
385
742
|
/**
|
|
386
743
|
* Returns the last element of a non-empty array.
|
|
387
744
|
*
|
|
388
|
-
* Accepts both mutable and readonly arrays. Does not mutate the original array.
|
|
389
|
-
*
|
|
390
745
|
* ### Example
|
|
391
746
|
*
|
|
392
747
|
* ```ts
|
|
393
748
|
* lastInArray(["a", "b", "c"]); // "c"
|
|
394
749
|
* ```
|
|
395
750
|
*
|
|
396
|
-
* @
|
|
751
|
+
* @group Accessors
|
|
397
752
|
*/
|
|
398
753
|
export const lastInArray = <T>(array: NonEmptyReadonlyArray<T>): T =>
|
|
399
754
|
array[array.length - 1];
|
|
400
755
|
|
|
401
756
|
/**
|
|
402
|
-
* Shifts
|
|
757
|
+
* Shifts (removes and returns) the first element from a non-empty mutable
|
|
758
|
+
* array.
|
|
403
759
|
*
|
|
404
760
|
* **Mutates** the original array.
|
|
405
761
|
*
|
|
406
762
|
* ### Example
|
|
407
763
|
*
|
|
408
764
|
* ```ts
|
|
409
|
-
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
765
|
+
* // Process a queue of callbacks
|
|
766
|
+
* const waitingQueue: Array<() => void> = [callback1, callback2];
|
|
767
|
+
* if (isNonEmptyArray(waitingQueue)) {
|
|
768
|
+
* shiftFromArray(waitingQueue)(); // Remove and immediately invoke
|
|
769
|
+
* }
|
|
412
770
|
* ```
|
|
413
771
|
*
|
|
414
|
-
* @
|
|
772
|
+
* @group Mutations
|
|
415
773
|
*/
|
|
416
|
-
export const
|
|
774
|
+
export const shiftFromArray = <T>(array: NonEmptyArray<T>): T =>
|
|
775
|
+
array.shift() as T;
|
|
417
776
|
|
|
418
777
|
/**
|
|
419
|
-
* Pops
|
|
778
|
+
* Pops (removes and returns) the last element from a non-empty mutable array.
|
|
420
779
|
*
|
|
421
780
|
* **Mutates** the original array.
|
|
422
781
|
*
|
|
423
782
|
* ### Example
|
|
424
783
|
*
|
|
425
784
|
* ```ts
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
785
|
+
* // Process a stack of callbacks (LIFO)
|
|
786
|
+
* const callbackStack: Array<() => void> = [callback1, callback2];
|
|
787
|
+
* if (isNonEmptyArray(callbackStack)) {
|
|
788
|
+
* popFromArray(callbackStack)(); // Remove and immediately invoke
|
|
789
|
+
* }
|
|
429
790
|
* ```
|
|
430
791
|
*
|
|
431
|
-
* @
|
|
792
|
+
* @group Mutations
|
|
432
793
|
*/
|
|
433
|
-
export const
|
|
794
|
+
export const popFromArray = <T>(array: NonEmptyArray<T>): T => array.pop() as T;
|