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
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type BoundedLengthArray,
|
|
3
|
+
type FixedLengthArray,
|
|
4
|
+
type FixedLengthTuple,
|
|
5
|
+
type MaxLengthArray,
|
|
6
|
+
type MinLengthArray,
|
|
7
|
+
type Seq,
|
|
8
|
+
} from 'ts-type-forge';
|
|
9
|
+
import { expectType } from '../../expect-type.mjs';
|
|
10
|
+
import { copy, create, seq, zeros } from './array-utils-creation.mjs';
|
|
11
|
+
import {
|
|
12
|
+
asBoundedLengthArray,
|
|
13
|
+
asFixedLengthArray,
|
|
14
|
+
asMaxLengthArray,
|
|
15
|
+
asMinLengthArray,
|
|
16
|
+
} from './array-utils-length-bounded-array-cast.mjs';
|
|
17
|
+
import {
|
|
18
|
+
set,
|
|
19
|
+
toFilled,
|
|
20
|
+
toRangeFilled,
|
|
21
|
+
toUpdated,
|
|
22
|
+
} from './array-utils-modification.mjs';
|
|
23
|
+
import {
|
|
24
|
+
map,
|
|
25
|
+
partition,
|
|
26
|
+
toReversed,
|
|
27
|
+
toSorted,
|
|
28
|
+
toSortedBy,
|
|
29
|
+
} from './array-utils-transformation.mjs';
|
|
30
|
+
|
|
31
|
+
describe('map preserves length constraints', () => {
|
|
32
|
+
test('a branded input keeps its constraint', () => {
|
|
33
|
+
const input: MinLengthArray<3, number> = asMinLengthArray(3, [0, 1, 2, 3]);
|
|
34
|
+
|
|
35
|
+
const result = map(input, (value) => `#${String(value)}`);
|
|
36
|
+
|
|
37
|
+
expectType<typeof result, MinLengthArray<3, string>>('~=');
|
|
38
|
+
|
|
39
|
+
// The structural prefix survives, so in-range access is not `undefined`.
|
|
40
|
+
const first: string = result[0];
|
|
41
|
+
|
|
42
|
+
assert.deepStrictEqual(first, '#0');
|
|
43
|
+
|
|
44
|
+
assert.deepStrictEqual<readonly string[]>(result, ['#0', '#1', '#2', '#3']);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('a fixed-length branded input keeps its exact length', () => {
|
|
48
|
+
const input: FixedLengthArray<3, number> = asFixedLengthArray(
|
|
49
|
+
3,
|
|
50
|
+
[255, 128, 0],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const result = map(input, (value) => value / 255);
|
|
54
|
+
|
|
55
|
+
expectType<typeof result, FixedLengthArray<3, number>>('~=');
|
|
56
|
+
|
|
57
|
+
const length: 3 = result.length;
|
|
58
|
+
|
|
59
|
+
assert.deepStrictEqual(length, 3);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('a minimum above the structural prefix cap survives, clamped like MinLengthArray itself', () => {
|
|
63
|
+
const input: MinLengthArray<12, number> = asMinLengthArray(
|
|
64
|
+
12,
|
|
65
|
+
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const result = map(input, String);
|
|
69
|
+
|
|
70
|
+
expectType<typeof result, MinLengthArray<12, string>>('~=');
|
|
71
|
+
|
|
72
|
+
assert.deepStrictEqual<readonly string[]>(result, [
|
|
73
|
+
'0',
|
|
74
|
+
'1',
|
|
75
|
+
'2',
|
|
76
|
+
'3',
|
|
77
|
+
'4',
|
|
78
|
+
'5',
|
|
79
|
+
'6',
|
|
80
|
+
'7',
|
|
81
|
+
'8',
|
|
82
|
+
'9',
|
|
83
|
+
'10',
|
|
84
|
+
'11',
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test('a plain tuple still maps element-wise', () => {
|
|
89
|
+
const result = map([1, 2, 3] as const, String);
|
|
90
|
+
|
|
91
|
+
expectType<typeof result, FixedLengthTuple<3, string>>('=');
|
|
92
|
+
|
|
93
|
+
assert.deepStrictEqual(result, ['1', '2', '3']);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('a plain array still maps to a plain array', () => {
|
|
97
|
+
const input: readonly number[] = [1, 2, 3] as const;
|
|
98
|
+
|
|
99
|
+
const result = map(input, String);
|
|
100
|
+
|
|
101
|
+
expectType<typeof result, readonly string[]>('=');
|
|
102
|
+
|
|
103
|
+
assert.deepStrictEqual(result, ['1', '2', '3']);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('an upper bound survives too', () => {
|
|
107
|
+
const input: MaxLengthArray<5, number> = asMaxLengthArray(5, [1, 2]);
|
|
108
|
+
|
|
109
|
+
const result = map(input, String);
|
|
110
|
+
|
|
111
|
+
expectType<typeof result, MaxLengthArray<5, string>>('~=');
|
|
112
|
+
|
|
113
|
+
assert.deepStrictEqual<readonly string[]>(result, ['1', '2']);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test('both bounds survive together', () => {
|
|
117
|
+
const input: BoundedLengthArray<2, 5, number> = asBoundedLengthArray(
|
|
118
|
+
2,
|
|
119
|
+
5,
|
|
120
|
+
[1, 2],
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
const result = map(input, String);
|
|
124
|
+
|
|
125
|
+
expectType<typeof result, BoundedLengthArray<2, 5, string>>('~=');
|
|
126
|
+
|
|
127
|
+
assert.deepStrictEqual<readonly string[]>(result, ['1', '2']);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('other length-preserving utilities preserve length constraints', () => {
|
|
132
|
+
test('toSorted keeps the brand', () => {
|
|
133
|
+
const input: FixedLengthArray<3, number> = asFixedLengthArray(3, [3, 1, 2]);
|
|
134
|
+
|
|
135
|
+
const result = toSorted(input, (x, y) => x - y);
|
|
136
|
+
|
|
137
|
+
expectType<typeof result, FixedLengthArray<3, number>>('~=');
|
|
138
|
+
|
|
139
|
+
assert.deepStrictEqual<readonly number[]>(result, [1, 2, 3]);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('toSortedBy keeps the brand', () => {
|
|
143
|
+
const input: MinLengthArray<3, number> = asMinLengthArray(3, [3, 1, 2]);
|
|
144
|
+
|
|
145
|
+
const result = toSortedBy(input, (x) => x);
|
|
146
|
+
|
|
147
|
+
expectType<typeof result, MinLengthArray<3, number>>('~=');
|
|
148
|
+
|
|
149
|
+
assert.deepStrictEqual<readonly number[]>(result, [1, 2, 3]);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test('set keeps the brand and widens only the element type', () => {
|
|
153
|
+
const input: MinLengthArray<3, number> = asMinLengthArray(3, [0, 1, 2]);
|
|
154
|
+
|
|
155
|
+
const result = set(input, 1, 'x');
|
|
156
|
+
|
|
157
|
+
expectType<typeof result, MinLengthArray<3, number | 'x'>>('~=');
|
|
158
|
+
|
|
159
|
+
assert.deepStrictEqual<readonly (number | string)[]>(result, [0, 'x', 2]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('toUpdated keeps the brand and widens only the element type', () => {
|
|
163
|
+
const input: MinLengthArray<3, number> = asMinLengthArray(3, [0, 1, 2]);
|
|
164
|
+
|
|
165
|
+
const result = toUpdated(input, 1, (prev) => `#${String(prev)}`);
|
|
166
|
+
|
|
167
|
+
expectType<typeof result, MinLengthArray<3, number | `#${string}`>>('~=');
|
|
168
|
+
|
|
169
|
+
assert.deepStrictEqual<readonly (number | string)[]>(result, [0, '#1', 2]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('toRangeFilled keeps the brand', () => {
|
|
173
|
+
const input: MinLengthArray<3, number> = asMinLengthArray(3, [0, 1, 2]);
|
|
174
|
+
|
|
175
|
+
const result = toRangeFilled(input, 'x', [0, 2]);
|
|
176
|
+
|
|
177
|
+
expectType<typeof result, MinLengthArray<3, number | 'x'>>('~=');
|
|
178
|
+
|
|
179
|
+
assert.deepStrictEqual<readonly (number | string)[]>(result, ['x', 'x', 2]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('toFilled keeps the brand with the fill type', () => {
|
|
183
|
+
const input: MinLengthArray<3, number> = asMinLengthArray(3, [0, 1, 2]);
|
|
184
|
+
|
|
185
|
+
const result = toFilled(input, 'x');
|
|
186
|
+
|
|
187
|
+
expectType<typeof result, MinLengthArray<3, 'x'>>('~=');
|
|
188
|
+
|
|
189
|
+
assert.deepStrictEqual<readonly string[]>(result, ['x', 'x', 'x']);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('toReversed keeps the brand', () => {
|
|
193
|
+
const input: MinLengthArray<3, number> = asMinLengthArray(3, [1, 2, 3, 4]);
|
|
194
|
+
|
|
195
|
+
const result = toReversed(input);
|
|
196
|
+
|
|
197
|
+
expectType<typeof result, MinLengthArray<3, number>>('~=');
|
|
198
|
+
|
|
199
|
+
// The structural prefix survives the reversal too.
|
|
200
|
+
const first: number = result[0];
|
|
201
|
+
|
|
202
|
+
assert.deepStrictEqual(first, 4);
|
|
203
|
+
|
|
204
|
+
assert.deepStrictEqual<readonly number[]>(result, [4, 3, 2, 1]);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test('toReversed still reverses a plain tuple at the type level', () => {
|
|
208
|
+
const result = toReversed([1, 'two', true] as const);
|
|
209
|
+
|
|
210
|
+
expectType<typeof result, readonly [true, 'two', 1]>('=');
|
|
211
|
+
|
|
212
|
+
assert.deepStrictEqual(result, [true, 'two', 1]);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test('copy keeps the brand, because it returns the input type', () => {
|
|
216
|
+
const input: FixedLengthArray<3, number> = asFixedLengthArray(3, [1, 2, 3]);
|
|
217
|
+
|
|
218
|
+
const result = copy(input);
|
|
219
|
+
|
|
220
|
+
expectType<typeof result, FixedLengthArray<3, number>>('=');
|
|
221
|
+
|
|
222
|
+
assert.deepStrictEqual<readonly number[]>(result, [1, 2, 3]);
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('partition reports the bounds of each chunk', () => {
|
|
227
|
+
test('a chunk is non-empty and at most the chunk size long', () => {
|
|
228
|
+
const result = partition([1, 2, 3, 4, 5] as const, 2);
|
|
229
|
+
|
|
230
|
+
expectType<
|
|
231
|
+
typeof result,
|
|
232
|
+
readonly BoundedLengthArray<1, 2, 1 | 2 | 3 | 4 | 5>[]
|
|
233
|
+
>('~=');
|
|
234
|
+
|
|
235
|
+
// The lower bound is the point: the first element of a chunk is never
|
|
236
|
+
// `undefined`, unlike with the plain `readonly E[]` this used to return.
|
|
237
|
+
const [firstChunk] = result;
|
|
238
|
+
|
|
239
|
+
// The outer array is unbounded, so this narrows away its `undefined`; the
|
|
240
|
+
// chunk's own first element needs no such check.
|
|
241
|
+
assert.isTrue(firstChunk !== undefined);
|
|
242
|
+
|
|
243
|
+
const first: 1 | 2 | 3 | 4 | 5 = firstChunk[0];
|
|
244
|
+
|
|
245
|
+
assert.deepStrictEqual(first, 1);
|
|
246
|
+
|
|
247
|
+
assert.deepStrictEqual<readonly (readonly number[])[]>(result, [
|
|
248
|
+
[1, 2],
|
|
249
|
+
[3, 4],
|
|
250
|
+
[5],
|
|
251
|
+
]);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe('creation utilities report an exact length past their tuple range', () => {
|
|
256
|
+
const zeros12 = zeros(12);
|
|
257
|
+
|
|
258
|
+
const seq12 = seq(12);
|
|
259
|
+
|
|
260
|
+
test('create is branded with the exact length', () => {
|
|
261
|
+
const result = create(12, 'x');
|
|
262
|
+
|
|
263
|
+
expectType<typeof result, FixedLengthArray<12, 'x'>>('~=');
|
|
264
|
+
|
|
265
|
+
assert.deepStrictEqual(result.length, 12);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test('a length still inside the tuple range keeps its exact tuple type', () => {
|
|
269
|
+
const result = create(3, 'x');
|
|
270
|
+
|
|
271
|
+
expectType<typeof result, FixedLengthTuple<3, 'x'>>('=');
|
|
272
|
+
|
|
273
|
+
assert.deepStrictEqual(result, ['x', 'x', 'x']);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test('zeros and seq keep their tuple type across their whole literal range', () => {
|
|
277
|
+
// `SizeType.ArgArr` only carries literals up to `SmallUint`, which these
|
|
278
|
+
// two already expand into a tuple, so there is no branded case to add.
|
|
279
|
+
expectType<typeof zeros12, FixedLengthTuple<12, 0>>('=');
|
|
280
|
+
|
|
281
|
+
expectType<typeof seq12, Seq<12>>('=');
|
|
282
|
+
|
|
283
|
+
assert.deepStrictEqual(zeros12.length, 12);
|
|
284
|
+
|
|
285
|
+
assert.deepStrictEqual(seq12.length, 12);
|
|
286
|
+
});
|
|
287
|
+
});
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
type ChangeArrayElement,
|
|
3
|
+
type ConstrainedList,
|
|
2
4
|
type FixedLengthTuple,
|
|
3
5
|
type Increment,
|
|
4
6
|
type IsFixedLengthList,
|
|
5
7
|
type NonEmptyArray,
|
|
6
|
-
type
|
|
8
|
+
type UnknownBrand,
|
|
7
9
|
} from 'ts-type-forge';
|
|
8
10
|
import { asPositiveUint32 } from '../../number/index.mjs';
|
|
9
11
|
import { castMutable } from '../../others/index.mjs';
|
|
@@ -27,27 +29,17 @@ import { copy, create } from './array-utils-creation.mjs';
|
|
|
27
29
|
* assert.deepStrictEqual(updated, [10, 25, 30]);
|
|
28
30
|
* ```
|
|
29
31
|
*/
|
|
30
|
-
export function set<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
):
|
|
35
|
-
? Readonly<{ [K in keyof Ar]: Ar[K] | V }>
|
|
36
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
37
|
-
? NonEmptyArray<Ar[number] | V>
|
|
38
|
-
: readonly (Ar[number] | V)[];
|
|
32
|
+
export function set<
|
|
33
|
+
const Ar extends readonly unknown[],
|
|
34
|
+
const I extends ArgArrayIndex<Ar>,
|
|
35
|
+
const V = Ar[number],
|
|
36
|
+
>(array: Ar, index: I, newValue: V): ReplacedAt<Ar, I, V>;
|
|
39
37
|
|
|
40
38
|
// curried version
|
|
41
|
-
export function set<const V>(
|
|
42
|
-
index:
|
|
39
|
+
export function set<const I extends SizeType.ArgArr, const V>(
|
|
40
|
+
index: I,
|
|
43
41
|
newValue: V,
|
|
44
|
-
): <const Ar extends readonly unknown[]>(
|
|
45
|
-
array: Ar,
|
|
46
|
-
) => IsFixedLengthList<Ar> extends true
|
|
47
|
-
? Readonly<{ [K in keyof Ar]: Ar[K] | V }>
|
|
48
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
49
|
-
? NonEmptyArray<Ar[number] | V>
|
|
50
|
-
: readonly (Ar[number] | V)[];
|
|
42
|
+
): <const Ar extends readonly unknown[]>(array: Ar) => ReplacedAt<Ar, I, V>;
|
|
51
43
|
|
|
52
44
|
export function set<E, const V = E>(
|
|
53
45
|
...args:
|
|
@@ -79,7 +71,7 @@ const setImpl = <E, const V = E>(
|
|
|
79
71
|
*
|
|
80
72
|
* const increased = Arr.toUpdated(temperatures, 1, (value) => value + 5);
|
|
81
73
|
*
|
|
82
|
-
* const incrementLast = Arr.toUpdated<number>(
|
|
74
|
+
* const incrementLast = Arr.toUpdated<2, number>(
|
|
83
75
|
* 2,
|
|
84
76
|
* (value) => value + 1,
|
|
85
77
|
* )(temperatures);
|
|
@@ -91,28 +83,15 @@ const setImpl = <E, const V = E>(
|
|
|
91
83
|
*/
|
|
92
84
|
export function toUpdated<
|
|
93
85
|
const Ar extends readonly unknown[],
|
|
86
|
+
const I extends ArgArrayIndex<Ar>,
|
|
94
87
|
const V = Ar[number],
|
|
95
|
-
>(
|
|
96
|
-
array: Ar,
|
|
97
|
-
index: ArgArrayIndex<Ar>,
|
|
98
|
-
updater: (prev: Ar[number]) => V,
|
|
99
|
-
): IsFixedLengthList<Ar> extends true
|
|
100
|
-
? Readonly<{ [K in keyof Ar]: Ar[K] | V }>
|
|
101
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
102
|
-
? NonEmptyArray<Ar[number] | V>
|
|
103
|
-
: readonly (Ar[number] | V)[];
|
|
88
|
+
>(array: Ar, index: I, updater: (prev: Ar[number]) => V): ReplacedAt<Ar, I, V>;
|
|
104
89
|
|
|
105
90
|
// curried version
|
|
106
|
-
export function toUpdated<E, const V = E>(
|
|
107
|
-
index:
|
|
91
|
+
export function toUpdated<const I extends SizeType.ArgArr, E, const V = E>(
|
|
92
|
+
index: I,
|
|
108
93
|
updater: (prev: E) => V,
|
|
109
|
-
): <const Ar extends readonly E[]>(
|
|
110
|
-
array: Ar,
|
|
111
|
-
) => IsFixedLengthList<Ar> extends true
|
|
112
|
-
? Readonly<{ [K in keyof Ar]: Ar[K] | V }>
|
|
113
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
114
|
-
? NonEmptyArray<Ar[number] | V>
|
|
115
|
-
: readonly (Ar[number] | V)[];
|
|
94
|
+
): <const Ar extends readonly E[]>(array: Ar) => ReplacedAt<Ar, I, V>;
|
|
116
95
|
|
|
117
96
|
export function toUpdated<E, V = E>(
|
|
118
97
|
...args:
|
|
@@ -376,25 +355,33 @@ const toUnshiftedImpl = <Ar extends readonly unknown[], const V>(
|
|
|
376
355
|
* assert.deepStrictEqual(filledCurried, ['x', 'x', 'x']);
|
|
377
356
|
* ```
|
|
378
357
|
*/
|
|
358
|
+
export function toFilled<
|
|
359
|
+
const Ar extends readonly unknown[] & UnknownBrand,
|
|
360
|
+
const V,
|
|
361
|
+
>(array: Ar, value: V): ChangeArrayElement<Ar, V>;
|
|
362
|
+
|
|
363
|
+
// For an array or tuple that carries no length brand the homomorphic mapping
|
|
364
|
+
// already covers every case the conditional spelled out — a fixed-length tuple
|
|
365
|
+
// keeps its length, `readonly [A, ...A[]]` stays non-empty, and `readonly A[]`
|
|
366
|
+
// stays unbounded. Stating it directly is what keeps this usable from a
|
|
367
|
+
// caller that is itself generic over the array: a conditional a *generic* `Ar`
|
|
368
|
+
// cannot decide stays deferred, and its branded branch is then not assignable
|
|
369
|
+
// to the caller's own mapping.
|
|
379
370
|
export function toFilled<const Ar extends readonly unknown[], const V>(
|
|
380
371
|
array: Ar,
|
|
381
372
|
value: V,
|
|
382
|
-
):
|
|
383
|
-
? FixedLengthTuple<Ar['length'], V>
|
|
384
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
385
|
-
? NonEmptyArray<V>
|
|
386
|
-
: readonly V[];
|
|
373
|
+
): Readonly<{ [K in keyof Ar]: V }>;
|
|
387
374
|
|
|
388
|
-
// curried version
|
|
375
|
+
// curried version — the two cases have to be overloads of the *returned*
|
|
376
|
+
// function, spelled as an intersection of function types (see `Arr.map`).
|
|
389
377
|
export function toFilled<const V>(
|
|
390
378
|
value: V,
|
|
391
|
-
): <const Ar extends readonly unknown[]>(
|
|
379
|
+
): (<const Ar extends readonly unknown[] & UnknownBrand>(
|
|
392
380
|
array: Ar,
|
|
393
|
-
) =>
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
: readonly V[];
|
|
381
|
+
) => ChangeArrayElement<Ar, V>) &
|
|
382
|
+
(<const Ar extends readonly unknown[]>(
|
|
383
|
+
array: Ar,
|
|
384
|
+
) => Readonly<{ [K in keyof Ar]: V }>);
|
|
398
385
|
|
|
399
386
|
export function toFilled<E>(
|
|
400
387
|
...args: readonly [array: readonly E[], value: E] | readonly [value: E]
|
|
@@ -428,6 +415,20 @@ const toFilledImpl = <E,>(array: readonly E[], value: E): readonly E[] =>
|
|
|
428
415
|
* assert.deepStrictEqual(filledPrefix, [8, 8, 2, 3, 4]);
|
|
429
416
|
* ```
|
|
430
417
|
*/
|
|
418
|
+
export function toRangeFilled<
|
|
419
|
+
const Ar extends readonly unknown[] & UnknownBrand,
|
|
420
|
+
const V,
|
|
421
|
+
>(
|
|
422
|
+
array: Ar,
|
|
423
|
+
value: V,
|
|
424
|
+
fillRange: readonly [
|
|
425
|
+
start: ArgArrayIndexWithNegative<Ar>,
|
|
426
|
+
end: ArgArrayIndexWithNegative<Ar>,
|
|
427
|
+
],
|
|
428
|
+
): ChangeArrayElement<Ar, V | Ar[number]>;
|
|
429
|
+
|
|
430
|
+
// See `toFilled` for why the unbranded case states the homomorphic mapping
|
|
431
|
+
// directly instead of going through a conditional.
|
|
431
432
|
export function toRangeFilled<const Ar extends readonly unknown[], const V>(
|
|
432
433
|
array: Ar,
|
|
433
434
|
value: V,
|
|
@@ -435,11 +436,7 @@ export function toRangeFilled<const Ar extends readonly unknown[], const V>(
|
|
|
435
436
|
start: ArgArrayIndexWithNegative<Ar>,
|
|
436
437
|
end: ArgArrayIndexWithNegative<Ar>,
|
|
437
438
|
],
|
|
438
|
-
):
|
|
439
|
-
? FixedLengthTuple<Ar['length'], V | Ar[number]>
|
|
440
|
-
: Ar extends NonEmptyTuple<unknown>
|
|
441
|
-
? NonEmptyArray<V | Ar[number]>
|
|
442
|
-
: readonly (V | Ar[number])[];
|
|
439
|
+
): Readonly<{ [K in keyof Ar]: V | Ar[number] }>;
|
|
443
440
|
|
|
444
441
|
// curried version
|
|
445
442
|
export function toRangeFilled<const V>(
|
|
@@ -448,13 +445,12 @@ export function toRangeFilled<const V>(
|
|
|
448
445
|
start: SizeType.ArgArrWithNegative,
|
|
449
446
|
end: SizeType.ArgArrWithNegative,
|
|
450
447
|
],
|
|
451
|
-
): <const Ar extends readonly unknown[]>(
|
|
448
|
+
): (<const Ar extends readonly unknown[] & UnknownBrand>(
|
|
452
449
|
array: Ar,
|
|
453
|
-
) =>
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
: readonly (V | Ar[number])[];
|
|
450
|
+
) => ChangeArrayElement<Ar, V | Ar[number]>) &
|
|
451
|
+
(<const Ar extends readonly unknown[]>(
|
|
452
|
+
array: Ar,
|
|
453
|
+
) => Readonly<{ [K in keyof Ar]: V | Ar[number] }>);
|
|
458
454
|
|
|
459
455
|
export function toRangeFilled<E, const V>(
|
|
460
456
|
...args:
|
|
@@ -499,3 +495,30 @@ const toRangeFilledImpl = <E, const V>(
|
|
|
499
495
|
|
|
500
496
|
return mut_cp;
|
|
501
497
|
};
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* The result of replacing the element at `I` of `Ar` with a `V`.
|
|
501
|
+
*
|
|
502
|
+
* `ConstrainedList.SetAt` replaces exactly one position when the index pins
|
|
503
|
+
* one, which is what makes `Arr.set(xs, 1, v)` report `readonly [a, V, c]`
|
|
504
|
+
* instead of widening every position to `a | V`.
|
|
505
|
+
*
|
|
506
|
+
* A union index needs the weaker answer, and this used to guard for it here:
|
|
507
|
+
* `ArgArrayIndex<Ar>` for a tuple is the union of its indices, so
|
|
508
|
+
* `Arr.set(tuple, someIndex, v)` with `someIndex: 0 | 2` binds `I = 0 | 2`, and
|
|
509
|
+
* the upstream `List.SetAt` used to answer `readonly [V, b, V]` — a type that
|
|
510
|
+
* *neither* possible runtime result satisfies, since the call replaces one
|
|
511
|
+
* position or the other and never both.
|
|
512
|
+
*
|
|
513
|
+
* ts-type-forge 9.1.1 fixes that at the source and widens the candidate
|
|
514
|
+
* positions itself, so the guard is gone. Upstream is the more precise of the
|
|
515
|
+
* two: it widens only the positions the index can name, where the guard here
|
|
516
|
+
* widened all of them — `Arr.set(t, i, 'x')` with `i: 0 | 2` on
|
|
517
|
+
* `readonly [1, 2, 3]` now keeps the middle element at `2` rather than
|
|
518
|
+
* reporting `2 | 'x'`.
|
|
519
|
+
*/
|
|
520
|
+
type ReplacedAt<
|
|
521
|
+
Ar extends readonly unknown[],
|
|
522
|
+
I extends number,
|
|
523
|
+
V,
|
|
524
|
+
> = ConstrainedList.SetAt<I, V, Ar>;
|