@evolu/common 8.10.0 → 8.11.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/dist/src/Config.d.ts +22 -22
- package/dist/src/Config.d.ts.map +1 -1
- package/dist/src/Console.d.ts +62 -7
- package/dist/src/Console.d.ts.map +1 -1
- package/dist/src/Console.js +20 -4
- package/dist/src/Crypto.d.ts +76 -4
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +55 -4
- package/dist/src/Error.d.ts +45 -0
- package/dist/src/Error.d.ts.map +1 -1
- package/dist/src/Error.js +69 -0
- package/dist/src/Fs.d.ts +92 -18
- package/dist/src/Fs.d.ts.map +1 -1
- package/dist/src/Fs.js +2 -0
- package/dist/src/Identicon.d.ts +2 -2
- package/dist/src/Identicon.js +2 -2
- package/dist/src/LeakDetector.d.ts +22 -3
- package/dist/src/LeakDetector.d.ts.map +1 -1
- package/dist/src/LeakDetector.js +12 -2
- package/dist/src/LockManager.d.ts +8 -0
- package/dist/src/LockManager.d.ts.map +1 -1
- package/dist/src/LockManager.js +6 -0
- package/dist/src/Object.d.ts.map +1 -1
- package/dist/src/Object.js +5 -0
- package/dist/src/Platform.d.ts +47 -7
- package/dist/src/Platform.d.ts.map +1 -1
- package/dist/src/Platform.js +24 -5
- package/dist/src/Random.d.ts +25 -2
- package/dist/src/Random.d.ts.map +1 -1
- package/dist/src/Random.js +14 -2
- package/dist/src/Resource.d.ts +156 -1
- package/dist/src/Resource.d.ts.map +1 -1
- package/dist/src/Resource.js +201 -72
- package/dist/src/Schedule.d.ts +11 -10
- package/dist/src/Schedule.d.ts.map +1 -1
- package/dist/src/Schedule.js +1 -1
- package/dist/src/Sqlite.d.ts +132 -16
- package/dist/src/Sqlite.d.ts.map +1 -1
- package/dist/src/Sqlite.js +63 -9
- package/dist/src/Task.d.ts +15 -4
- package/dist/src/Task.d.ts.map +1 -1
- package/dist/src/Task.js +41 -15
- package/dist/src/Test.d.ts +9 -0
- package/dist/src/Test.d.ts.map +1 -1
- package/dist/src/Test.js +4 -0
- package/dist/src/Time.d.ts +106 -9
- package/dist/src/Time.d.ts.map +1 -1
- package/dist/src/Time.js +55 -4
- package/dist/src/Type.d.ts +1455 -1310
- package/dist/src/Type.d.ts.map +1 -1
- package/dist/src/Type.js +1274 -517
- package/dist/src/WebSocket.d.ts +164 -13
- package/dist/src/WebSocket.d.ts.map +1 -1
- package/dist/src/WebSocket.js +133 -24
- package/dist/src/Worker.d.ts +90 -8
- package/dist/src/Worker.d.ts.map +1 -1
- package/dist/src/Worker.js +28 -2
- package/dist/src/index.d.ts +6 -7
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -3
- package/dist/src/local-first/Db.d.ts +52 -3
- package/dist/src/local-first/Db.d.ts.map +1 -1
- package/dist/src/local-first/Db.js +412 -137
- package/dist/src/local-first/Evolu.d.ts +336 -211
- package/dist/src/local-first/Evolu.d.ts.map +1 -1
- package/dist/src/local-first/Evolu.js +102 -15
- package/dist/src/local-first/Owner.d.ts +13 -30
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +13 -30
- package/dist/src/local-first/Protocol.d.ts +94 -16
- package/dist/src/local-first/Protocol.d.ts.map +1 -1
- package/dist/src/local-first/Protocol.js +118 -38
- package/dist/src/local-first/Query.d.ts +8 -15
- package/dist/src/local-first/Query.d.ts.map +1 -1
- package/dist/src/local-first/Schema.d.ts +335 -21
- package/dist/src/local-first/Schema.d.ts.map +1 -1
- package/dist/src/local-first/Schema.js +214 -17
- package/dist/src/local-first/Shared.d.ts +537 -22
- package/dist/src/local-first/Shared.d.ts.map +1 -1
- package/dist/src/local-first/Shared.js +1437 -234
- package/dist/src/local-first/Storage.d.ts +192 -14
- package/dist/src/local-first/Storage.d.ts.map +1 -1
- package/dist/src/local-first/Storage.js +81 -20
- package/dist/src/local-first/Timestamp.d.ts +392 -41
- package/dist/src/local-first/Timestamp.d.ts.map +1 -1
- package/dist/src/local-first/Timestamp.js +403 -81
- package/dist/src/local-first/index.d.ts +0 -1
- package/dist/src/local-first/index.d.ts.map +1 -1
- package/dist/src/local-first/index.js +0 -1
- package/package.json +1 -1
- package/src/Assert.test.ts +2 -5
- package/src/Config.test.ts +2 -6
- package/src/Config.ts +133 -133
- package/src/Console.ts +62 -7
- package/src/Crypto.ts +76 -4
- package/src/Eq.test.ts +2 -3
- package/src/Error.test.ts +76 -3
- package/src/Error.ts +71 -0
- package/src/Fs.ts +92 -18
- package/src/Identicon.ts +2 -2
- package/src/LeakDetector.ts +22 -3
- package/src/LockManager.ts +8 -0
- package/src/Object.test.ts +27 -12
- package/src/Object.ts +5 -0
- package/src/Platform.ts +50 -8
- package/src/Random.ts +25 -2
- package/src/Resource.test.ts +837 -0
- package/src/Resource.ts +235 -15
- package/src/Schedule.test.ts +50 -12
- package/src/Schedule.ts +24 -14
- package/src/Sqlite.ts +137 -17
- package/src/Task.test.ts +189 -8
- package/src/Task.ts +56 -17
- package/src/Test.ts +9 -0
- package/src/Time.ts +106 -9
- package/src/Type.test.ts +946 -1028
- package/src/Type.ts +4195 -3136
- package/src/Types.test.ts +4 -14
- package/src/WebSocket.ts +313 -40
- package/src/Worker.ts +90 -8
- package/src/index.ts +15 -6
- package/src/local-first/Db.ts +644 -339
- package/src/local-first/Evolu.test.ts +686 -21
- package/src/local-first/Evolu.ts +450 -228
- package/src/local-first/Owner.ts +13 -30
- package/src/local-first/Protocol.test.ts +617 -10
- package/src/local-first/Protocol.ts +196 -72
- package/src/local-first/Query.ts +8 -15
- package/src/local-first/Schema.test.ts +143 -0
- package/src/local-first/Schema.ts +363 -24
- package/src/local-first/Shared.test.ts +7731 -559
- package/src/local-first/Shared.ts +2036 -267
- package/src/local-first/Storage.ts +218 -32
- package/src/local-first/Timestamp.test.ts +344 -70
- package/src/local-first/Timestamp.ts +434 -118
- package/src/local-first/index.ts +0 -1
- package/dist/src/local-first/Error.d.ts +0 -12
- package/dist/src/local-first/Error.d.ts.map +0 -1
- package/dist/src/local-first/Error.js +0 -6
- package/dist/src/local-first/LocalAuth.d.ts +0 -150
- package/dist/src/local-first/LocalAuth.d.ts.map +0 -1
- package/dist/src/local-first/LocalAuth.js +0 -179
- package/src/local-first/Error.ts +0 -17
- package/src/local-first/LocalAuth.ts +0 -457
package/src/Type.test.ts
CHANGED
|
@@ -315,6 +315,9 @@ import {
|
|
|
315
315
|
type RecordNotRecordError,
|
|
316
316
|
type RecordType,
|
|
317
317
|
type RecordValueIssue,
|
|
318
|
+
type RootMapType,
|
|
319
|
+
type RootRecordType,
|
|
320
|
+
type RootTupleType,
|
|
318
321
|
type SetElementIssue,
|
|
319
322
|
type SetElementsError,
|
|
320
323
|
type SetError,
|
|
@@ -1171,6 +1174,263 @@ describe("Type operations", () => {
|
|
|
1171
1174
|
});
|
|
1172
1175
|
});
|
|
1173
1176
|
|
|
1177
|
+
describe("identity Types", () => {
|
|
1178
|
+
// Transforms returning their input keep the Result-based container paths, so
|
|
1179
|
+
// they show that containers of identity Types validate the same way.
|
|
1180
|
+
const SameString = transform("SameString", String, String, {
|
|
1181
|
+
from: (value) => ok(value),
|
|
1182
|
+
to: (value) => value,
|
|
1183
|
+
});
|
|
1184
|
+
const SameNumber = transform("SameNumber", Number, Number, {
|
|
1185
|
+
from: (value) => ok(value),
|
|
1186
|
+
to: (value) => value,
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
const withProperty = <T extends object>(
|
|
1190
|
+
value: T,
|
|
1191
|
+
key: PropertyKey,
|
|
1192
|
+
descriptor: PropertyDescriptor,
|
|
1193
|
+
): T => globalThis.Object.defineProperty(value, key, descriptor);
|
|
1194
|
+
const accessor = { get: () => "value", enumerable: true };
|
|
1195
|
+
const nonEnumerable = { value: "value", enumerable: false };
|
|
1196
|
+
const symbol = globalThis.Symbol("key");
|
|
1197
|
+
|
|
1198
|
+
// A Map without own properties whose inherited iterator repeats keys.
|
|
1199
|
+
const setupRepeatedKeyMap = (
|
|
1200
|
+
entries: ReadonlyArray<readonly [unknown, unknown]>,
|
|
1201
|
+
): ReadonlyMap<unknown, unknown> =>
|
|
1202
|
+
globalThis.Object.create(
|
|
1203
|
+
globalThis.Object.create(Map.prototype, {
|
|
1204
|
+
[globalThis.Symbol.iterator]: { value: () => entries.values() },
|
|
1205
|
+
}) as object,
|
|
1206
|
+
) as ReadonlyMap<unknown, unknown>;
|
|
1207
|
+
|
|
1208
|
+
const assertSameValidation = (
|
|
1209
|
+
identityType: TypeNode,
|
|
1210
|
+
decodingType: TypeNode,
|
|
1211
|
+
values: ReadonlyArray<unknown>,
|
|
1212
|
+
): void => {
|
|
1213
|
+
for (const value of values) {
|
|
1214
|
+
for (const options of [
|
|
1215
|
+
{ errors: "first" },
|
|
1216
|
+
{ errors: "all" },
|
|
1217
|
+
] satisfies ReadonlyArray<ValidationOptions>) {
|
|
1218
|
+
const result = identityType.fromUnknown(value, options);
|
|
1219
|
+
// Unlike assertEqual, this compares symbol keys of error records.
|
|
1220
|
+
nodeAssert.deepStrictEqual(
|
|
1221
|
+
result,
|
|
1222
|
+
decodingType.fromUnknown(value, options),
|
|
1223
|
+
);
|
|
1224
|
+
if (result.ok) assertSame(result.value, value);
|
|
1225
|
+
}
|
|
1226
|
+
assertSame(identityType.is(value), decodingType.is(value));
|
|
1227
|
+
|
|
1228
|
+
// Output validation of a transform wraps its errors, so only the identity
|
|
1229
|
+
// Type is expected to report its decoding error.
|
|
1230
|
+
const outputError = (type: TypeNode) => {
|
|
1231
|
+
try {
|
|
1232
|
+
assertType(type, value);
|
|
1233
|
+
return undefined;
|
|
1234
|
+
} catch (error) {
|
|
1235
|
+
return (error as Error).cause;
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
const result = identityType.fromUnknown(value);
|
|
1239
|
+
nodeAssert.deepStrictEqual(
|
|
1240
|
+
outputError(identityType),
|
|
1241
|
+
result.ok ? undefined : result.error,
|
|
1242
|
+
);
|
|
1243
|
+
assertSame(outputError(decodingType) === undefined, result.ok);
|
|
1244
|
+
}
|
|
1245
|
+
};
|
|
1246
|
+
|
|
1247
|
+
it("validate collections like decoding collections", () => {
|
|
1248
|
+
assertSameValidation(array(String), array(SameString), [
|
|
1249
|
+
["a", "b"],
|
|
1250
|
+
["a", 1, "b", 2],
|
|
1251
|
+
globalThis.Object.assign(createMutableArray<unknown>(3), {
|
|
1252
|
+
0: "a",
|
|
1253
|
+
2: "b",
|
|
1254
|
+
}),
|
|
1255
|
+
withProperty(["a", "b"], 1, accessor),
|
|
1256
|
+
globalThis.Object.assign(["a"], { extra: 1 }),
|
|
1257
|
+
1,
|
|
1258
|
+
]);
|
|
1259
|
+
assertSameValidation(set(String), set(SameString), [
|
|
1260
|
+
new Set(["a", "b"]),
|
|
1261
|
+
new Set(["a", 1, 2]),
|
|
1262
|
+
withProperty(new Set(["a", 1]), "extra", { value: 1 }),
|
|
1263
|
+
globalThis.Object.assign(new Set(["a", 1]), { extra: 1, other: 2 }),
|
|
1264
|
+
1,
|
|
1265
|
+
]);
|
|
1266
|
+
assertSameValidation(map(String, Number), map(SameString, SameNumber), [
|
|
1267
|
+
new Map([["a", 1]]),
|
|
1268
|
+
new Map<unknown, unknown>([
|
|
1269
|
+
["a", 1],
|
|
1270
|
+
[1, "b"],
|
|
1271
|
+
["c", "d"],
|
|
1272
|
+
]),
|
|
1273
|
+
withProperty(new Map<unknown, unknown>([[1, "b"]]), "extra", {
|
|
1274
|
+
value: 1,
|
|
1275
|
+
}),
|
|
1276
|
+
globalThis.Object.assign(new Map<unknown, unknown>([[1, "b"]]), {
|
|
1277
|
+
extra: 1,
|
|
1278
|
+
other: 2,
|
|
1279
|
+
}),
|
|
1280
|
+
setupRepeatedKeyMap([
|
|
1281
|
+
["a", 1],
|
|
1282
|
+
["a", 2],
|
|
1283
|
+
]),
|
|
1284
|
+
setupRepeatedKeyMap([
|
|
1285
|
+
[1, 1],
|
|
1286
|
+
[1, 2],
|
|
1287
|
+
]),
|
|
1288
|
+
setupRepeatedKeyMap([
|
|
1289
|
+
["a", 1],
|
|
1290
|
+
["b", "x"],
|
|
1291
|
+
[1, 2],
|
|
1292
|
+
["a", 2],
|
|
1293
|
+
]),
|
|
1294
|
+
1,
|
|
1295
|
+
]);
|
|
1296
|
+
assertSameValidation(tuple(String, Number), tuple(SameString, SameNumber), [
|
|
1297
|
+
["a", 1],
|
|
1298
|
+
[1, "b"],
|
|
1299
|
+
["a"],
|
|
1300
|
+
globalThis.Object.assign(["a", 1], { extra: 1 }),
|
|
1301
|
+
1,
|
|
1302
|
+
]);
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
it("reject values without the expected class or tag", () => {
|
|
1306
|
+
class User {
|
|
1307
|
+
readonly name = "Ada";
|
|
1308
|
+
}
|
|
1309
|
+
const UserInstance = instanceOf(User);
|
|
1310
|
+
|
|
1311
|
+
assertFalse(Date.is(1));
|
|
1312
|
+
assertEqual(
|
|
1313
|
+
array(Date).fromUnknown([new globalThis.Date(0), 1], { errors: "all" }),
|
|
1314
|
+
err({
|
|
1315
|
+
type: "Array",
|
|
1316
|
+
reason: {
|
|
1317
|
+
kind: "Items",
|
|
1318
|
+
issues: [
|
|
1319
|
+
{
|
|
1320
|
+
kind: "Element",
|
|
1321
|
+
index: 1,
|
|
1322
|
+
error: { type: "ObjectTag", expected: "Date", value: 1 },
|
|
1323
|
+
},
|
|
1324
|
+
],
|
|
1325
|
+
},
|
|
1326
|
+
}),
|
|
1327
|
+
);
|
|
1328
|
+
assertEqual(
|
|
1329
|
+
array(UserInstance).fromUnknown([new User(), 1], { errors: "all" }),
|
|
1330
|
+
err({
|
|
1331
|
+
type: "Array",
|
|
1332
|
+
reason: {
|
|
1333
|
+
kind: "Items",
|
|
1334
|
+
issues: [
|
|
1335
|
+
{
|
|
1336
|
+
kind: "Element",
|
|
1337
|
+
index: 1,
|
|
1338
|
+
error: { type: "InstanceOf", constructorName: "User", value: 1 },
|
|
1339
|
+
},
|
|
1340
|
+
],
|
|
1341
|
+
},
|
|
1342
|
+
}),
|
|
1343
|
+
);
|
|
1344
|
+
});
|
|
1345
|
+
|
|
1346
|
+
it("validate objects and records like decoding ones", () => {
|
|
1347
|
+
const invalidObjects = [
|
|
1348
|
+
1,
|
|
1349
|
+
globalThis.Object.create({}),
|
|
1350
|
+
{},
|
|
1351
|
+
{ a: "x", b: "x", c: 1, [symbol]: 1 },
|
|
1352
|
+
withProperty({}, "a", accessor),
|
|
1353
|
+
withProperty({}, "a", nonEnumerable),
|
|
1354
|
+
];
|
|
1355
|
+
const invalidRecords = [
|
|
1356
|
+
{ a: 1, c: "y", [symbol]: 1 },
|
|
1357
|
+
withProperty({ a: 1 }, "c", accessor),
|
|
1358
|
+
withProperty({ a: 1 }, "c", nonEnumerable),
|
|
1359
|
+
];
|
|
1360
|
+
|
|
1361
|
+
assertSameValidation(
|
|
1362
|
+
object({ a: Number, b: optional(String) }),
|
|
1363
|
+
object({ a: SameNumber, b: optional(SameString) }),
|
|
1364
|
+
[{ a: 1 }, { a: 1, b: "x" }, { b: "x", a: 1 }, ...invalidObjects],
|
|
1365
|
+
);
|
|
1366
|
+
assertSameValidation(
|
|
1367
|
+
record(String, Number),
|
|
1368
|
+
record(SameString, SameNumber),
|
|
1369
|
+
[{ a: 1 }, new globalThis.Date(0), ...invalidObjects, ...invalidRecords],
|
|
1370
|
+
);
|
|
1371
|
+
for (const decodingType of [
|
|
1372
|
+
object({ a: SameNumber }, record(String, Number)),
|
|
1373
|
+
object({ a: Number }, record(String, SameNumber)),
|
|
1374
|
+
]) {
|
|
1375
|
+
assertSameValidation(
|
|
1376
|
+
object({ a: Number }, record(String, Number)),
|
|
1377
|
+
decodingType,
|
|
1378
|
+
[{ a: 1, c: 1 }, ...invalidObjects, ...invalidRecords],
|
|
1379
|
+
);
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
it("copies identity Record properties into a decoded object", () => {
|
|
1384
|
+
const Model = object(
|
|
1385
|
+
{ count: setupNumberFromString() },
|
|
1386
|
+
record(String, union(String, Number)),
|
|
1387
|
+
);
|
|
1388
|
+
|
|
1389
|
+
const result = Model.fromUnknown({ count: "1", first: 2, second: "3" });
|
|
1390
|
+
|
|
1391
|
+
assertOk(result, { count: 1, first: 2, second: "3" });
|
|
1392
|
+
assertSame(globalThis.Object.getPrototypeOf(result.value), null);
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
it("validate unions like decoding unions", () => {
|
|
1396
|
+
const Created = object({ type: literal("Created"), name: String });
|
|
1397
|
+
const Deleted = object({ type: literal("Deleted"), id: Number });
|
|
1398
|
+
|
|
1399
|
+
assertSameValidation(
|
|
1400
|
+
union(String, Number, null),
|
|
1401
|
+
union(SameString, SameNumber, null),
|
|
1402
|
+
["a", 1, null, true],
|
|
1403
|
+
);
|
|
1404
|
+
// Lazy Types have no identity check, so this Union input validates
|
|
1405
|
+
// members with Results.
|
|
1406
|
+
assertSameValidation(
|
|
1407
|
+
union(String, Number).parent,
|
|
1408
|
+
union(
|
|
1409
|
+
lazy(() => String),
|
|
1410
|
+
Number,
|
|
1411
|
+
).parent,
|
|
1412
|
+
["a", 1, true],
|
|
1413
|
+
);
|
|
1414
|
+
assertSameValidation(
|
|
1415
|
+
array(discriminatedUnion(Created, Deleted)),
|
|
1416
|
+
array(
|
|
1417
|
+
discriminatedUnion(
|
|
1418
|
+
object({ type: literal("Created"), name: SameString }),
|
|
1419
|
+
Deleted,
|
|
1420
|
+
),
|
|
1421
|
+
),
|
|
1422
|
+
[
|
|
1423
|
+
[
|
|
1424
|
+
{ type: "Created", name: "Ada" },
|
|
1425
|
+
{ type: "Deleted", id: 1 },
|
|
1426
|
+
],
|
|
1427
|
+
[{ type: "Created", name: 1 }, { type: "Deleted" }, { type: "Moved" }],
|
|
1428
|
+
[1],
|
|
1429
|
+
],
|
|
1430
|
+
);
|
|
1431
|
+
});
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1174
1434
|
describe("Type.orThrow", () => {
|
|
1175
1435
|
it("formats a returned validation error once and preserves its identity", () => {
|
|
1176
1436
|
const cause = { type: "Text", value: "invalid" } as const;
|
|
@@ -1666,6 +1926,27 @@ describe("Standard Schema", () => {
|
|
|
1666
1926
|
],
|
|
1667
1927
|
});
|
|
1668
1928
|
});
|
|
1929
|
+
|
|
1930
|
+
it("prefixes nested JsonValue and Data issue paths", async () => {
|
|
1931
|
+
const Model = object({ json: JsonValue, data: array(Data) });
|
|
1932
|
+
const result = await Model["~standard"].validate({
|
|
1933
|
+
json: { values: [globalThis.Number.NaN] },
|
|
1934
|
+
data: [1, { value: /value/u }],
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
assertEqual(result, {
|
|
1938
|
+
issues: [
|
|
1939
|
+
{
|
|
1940
|
+
message: "A JSON number must be finite.",
|
|
1941
|
+
path: ["json", "values", 0],
|
|
1942
|
+
},
|
|
1943
|
+
{
|
|
1944
|
+
message: "A Data Object has an unexpected prototype.",
|
|
1945
|
+
path: ["data", 1, "value"],
|
|
1946
|
+
},
|
|
1947
|
+
],
|
|
1948
|
+
});
|
|
1949
|
+
});
|
|
1669
1950
|
});
|
|
1670
1951
|
|
|
1671
1952
|
describe("assertType", () => {
|
|
@@ -1675,7 +1956,7 @@ describe("assertType", () => {
|
|
|
1675
1956
|
assertType<never, never>();
|
|
1676
1957
|
assertType<{ readonly value?: string }, { readonly value?: string }>();
|
|
1677
1958
|
|
|
1678
|
-
|
|
1959
|
+
void (() => {
|
|
1679
1960
|
// @ts-expect-error ⛔ assertType error: Actual and expected types must be identical
|
|
1680
1961
|
assertType<"value", string>();
|
|
1681
1962
|
// @ts-expect-error ⛔ assertType error: Actual and expected types must be identical
|
|
@@ -1687,13 +1968,7 @@ describe("assertType", () => {
|
|
|
1687
1968
|
{ readonly value?: string },
|
|
1688
1969
|
{ readonly value: string | undefined }
|
|
1689
1970
|
>();
|
|
1690
|
-
};
|
|
1691
|
-
assertType<
|
|
1692
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
1693
|
-
? true
|
|
1694
|
-
: false,
|
|
1695
|
-
true
|
|
1696
|
-
>();
|
|
1971
|
+
});
|
|
1697
1972
|
});
|
|
1698
1973
|
|
|
1699
1974
|
it("narrows values that satisfy a Type Output", () => {
|
|
@@ -2279,7 +2554,7 @@ describe("localizeTypes", () => {
|
|
|
2279
2554
|
"Localized String.",
|
|
2280
2555
|
);
|
|
2281
2556
|
|
|
2282
|
-
|
|
2557
|
+
void (() => {
|
|
2283
2558
|
localizeTypes(
|
|
2284
2559
|
{ Strings },
|
|
2285
2560
|
{
|
|
@@ -2294,14 +2569,7 @@ describe("localizeTypes", () => {
|
|
|
2294
2569
|
missingSet: { String: () => "String." },
|
|
2295
2570
|
},
|
|
2296
2571
|
);
|
|
2297
|
-
};
|
|
2298
|
-
|
|
2299
|
-
assertType<
|
|
2300
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2301
|
-
? true
|
|
2302
|
-
: false,
|
|
2303
|
-
true
|
|
2304
|
-
>();
|
|
2572
|
+
});
|
|
2305
2573
|
});
|
|
2306
2574
|
|
|
2307
2575
|
it("localizes a Union summary and its retained member failures", () => {
|
|
@@ -2332,7 +2600,7 @@ describe("localizeTypes", () => {
|
|
|
2332
2600
|
"Localized String.",
|
|
2333
2601
|
);
|
|
2334
2602
|
|
|
2335
|
-
|
|
2603
|
+
void (() => {
|
|
2336
2604
|
localizeTypes(
|
|
2337
2605
|
{ Value },
|
|
2338
2606
|
{
|
|
@@ -2340,14 +2608,7 @@ describe("localizeTypes", () => {
|
|
|
2340
2608
|
test: { Union: () => "Union." },
|
|
2341
2609
|
},
|
|
2342
2610
|
);
|
|
2343
|
-
};
|
|
2344
|
-
|
|
2345
|
-
assertType<
|
|
2346
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2347
|
-
? true
|
|
2348
|
-
: false,
|
|
2349
|
-
true
|
|
2350
|
-
>();
|
|
2611
|
+
});
|
|
2351
2612
|
});
|
|
2352
2613
|
|
|
2353
2614
|
it("localizes nested Union details and can localize the result again", async () => {
|
|
@@ -2419,7 +2680,7 @@ describe("localizeTypes", () => {
|
|
|
2419
2680
|
"Localized Literal.",
|
|
2420
2681
|
);
|
|
2421
2682
|
|
|
2422
|
-
|
|
2683
|
+
void (() => {
|
|
2423
2684
|
localizeTypes(
|
|
2424
2685
|
{ Value },
|
|
2425
2686
|
{
|
|
@@ -2431,14 +2692,7 @@ describe("localizeTypes", () => {
|
|
|
2431
2692
|
},
|
|
2432
2693
|
},
|
|
2433
2694
|
);
|
|
2434
|
-
};
|
|
2435
|
-
|
|
2436
|
-
assertType<
|
|
2437
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2438
|
-
? true
|
|
2439
|
-
: false,
|
|
2440
|
-
true
|
|
2441
|
-
>();
|
|
2695
|
+
});
|
|
2442
2696
|
});
|
|
2443
2697
|
|
|
2444
2698
|
it("localizes fallible members of an infallible Union", () => {
|
|
@@ -2458,7 +2712,7 @@ describe("localizeTypes", () => {
|
|
|
2458
2712
|
"Localized String.",
|
|
2459
2713
|
);
|
|
2460
2714
|
|
|
2461
|
-
|
|
2715
|
+
void (() => {
|
|
2462
2716
|
localizeTypes(
|
|
2463
2717
|
{ Value },
|
|
2464
2718
|
{
|
|
@@ -2466,14 +2720,7 @@ describe("localizeTypes", () => {
|
|
|
2466
2720
|
test: {},
|
|
2467
2721
|
},
|
|
2468
2722
|
);
|
|
2469
|
-
};
|
|
2470
|
-
|
|
2471
|
-
assertType<
|
|
2472
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2473
|
-
? true
|
|
2474
|
-
: false,
|
|
2475
|
-
true
|
|
2476
|
-
>();
|
|
2723
|
+
});
|
|
2477
2724
|
});
|
|
2478
2725
|
|
|
2479
2726
|
it("requires fallible Types reachable only through reflection", () => {
|
|
@@ -2509,7 +2756,7 @@ describe("localizeTypes", () => {
|
|
|
2509
2756
|
"Localized String.",
|
|
2510
2757
|
);
|
|
2511
2758
|
|
|
2512
|
-
|
|
2759
|
+
void (() => {
|
|
2513
2760
|
localizeTypes(
|
|
2514
2761
|
{ Model, NonEmptyValues },
|
|
2515
2762
|
{
|
|
@@ -2521,14 +2768,7 @@ describe("localizeTypes", () => {
|
|
|
2521
2768
|
},
|
|
2522
2769
|
},
|
|
2523
2770
|
);
|
|
2524
|
-
};
|
|
2525
|
-
|
|
2526
|
-
assertType<
|
|
2527
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2528
|
-
? true
|
|
2529
|
-
: false,
|
|
2530
|
-
true
|
|
2531
|
-
>();
|
|
2771
|
+
});
|
|
2532
2772
|
});
|
|
2533
2773
|
|
|
2534
2774
|
it("routes DiscriminatedUnion member errors", () => {
|
|
@@ -2607,7 +2847,7 @@ describe("localizeTypes", () => {
|
|
|
2607
2847
|
},
|
|
2608
2848
|
).test.Refined;
|
|
2609
2849
|
const result = LocalizedRefined.fromUnknown("value");
|
|
2610
|
-
|
|
2850
|
+
void (() => {
|
|
2611
2851
|
localizeTypes(
|
|
2612
2852
|
{ Refined },
|
|
2613
2853
|
{
|
|
@@ -2615,7 +2855,7 @@ describe("localizeTypes", () => {
|
|
|
2615
2855
|
test: { String: () => "Localized String." },
|
|
2616
2856
|
},
|
|
2617
2857
|
);
|
|
2618
|
-
};
|
|
2858
|
+
});
|
|
2619
2859
|
|
|
2620
2860
|
assertErr(result);
|
|
2621
2861
|
assertEqual(
|
|
@@ -2625,12 +2865,6 @@ describe("localizeTypes", () => {
|
|
|
2625
2865
|
assertEqual(await LocalizedRefined["~standard"].validate("value"), {
|
|
2626
2866
|
issues: [{ message: "Localized Refined.", path: [] }],
|
|
2627
2867
|
});
|
|
2628
|
-
assertType<
|
|
2629
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2630
|
-
? true
|
|
2631
|
-
: false,
|
|
2632
|
-
true
|
|
2633
|
-
>();
|
|
2634
2868
|
});
|
|
2635
2869
|
|
|
2636
2870
|
it("routes custom root TypeOf errors by their expected Types", async () => {
|
|
@@ -2837,7 +3071,7 @@ describe("localizeTypes", () => {
|
|
|
2837
3071
|
it("requires plain string-keyed maps", () => {
|
|
2838
3072
|
const selected = globalThis.Symbol("selected");
|
|
2839
3073
|
const locale = globalThis.Symbol("locale");
|
|
2840
|
-
|
|
3074
|
+
void (() => {
|
|
2841
3075
|
localizeTypes(
|
|
2842
3076
|
// @ts-expect-error Selected Type names must be strings.
|
|
2843
3077
|
{ [selected]: String },
|
|
@@ -2848,7 +3082,7 @@ describe("localizeTypes", () => {
|
|
|
2848
3082
|
// @ts-expect-error Locale names must be strings.
|
|
2849
3083
|
{ [locale]: { String: () => "String." } },
|
|
2850
3084
|
);
|
|
2851
|
-
};
|
|
3085
|
+
});
|
|
2852
3086
|
|
|
2853
3087
|
const selectedByName = { [selected]: String } as unknown as {
|
|
2854
3088
|
readonly String: typeof String;
|
|
@@ -2929,13 +3163,6 @@ describe("localizeTypes", () => {
|
|
|
2929
3163
|
"localizeTypes maps must be plain objects with own enumerable string-keyed data properties.",
|
|
2930
3164
|
),
|
|
2931
3165
|
);
|
|
2932
|
-
|
|
2933
|
-
assertType<
|
|
2934
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2935
|
-
? true
|
|
2936
|
-
: false,
|
|
2937
|
-
true
|
|
2938
|
-
>();
|
|
2939
3166
|
});
|
|
2940
3167
|
|
|
2941
3168
|
it("requires exactly the reachable formatters", () => {
|
|
@@ -2949,7 +3176,7 @@ describe("localizeTypes", () => {
|
|
|
2949
3176
|
...formatters,
|
|
2950
3177
|
Number: () => "Number.",
|
|
2951
3178
|
};
|
|
2952
|
-
|
|
3179
|
+
void (() => {
|
|
2953
3180
|
localizeTypes({ Label }, { valid: formatters });
|
|
2954
3181
|
localizeTypes(
|
|
2955
3182
|
{ Label },
|
|
@@ -2976,14 +3203,7 @@ describe("localizeTypes", () => {
|
|
|
2976
3203
|
excessStored: excessFormatters,
|
|
2977
3204
|
},
|
|
2978
3205
|
);
|
|
2979
|
-
};
|
|
2980
|
-
|
|
2981
|
-
assertType<
|
|
2982
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
2983
|
-
? true
|
|
2984
|
-
: false,
|
|
2985
|
-
true
|
|
2986
|
-
>();
|
|
3206
|
+
});
|
|
2987
3207
|
});
|
|
2988
3208
|
|
|
2989
3209
|
it("requires formatters for every union-typed selection branch", () => {
|
|
@@ -3015,19 +3235,12 @@ describe("localizeTypes", () => {
|
|
|
3015
3235
|
"Localized Number.",
|
|
3016
3236
|
);
|
|
3017
3237
|
|
|
3018
|
-
|
|
3238
|
+
void ((selected: Selected) => {
|
|
3019
3239
|
localizeTypes(selected, {
|
|
3020
3240
|
// @ts-expect-error Every possible selection branch requires its formatter.
|
|
3021
3241
|
test: {},
|
|
3022
3242
|
});
|
|
3023
|
-
};
|
|
3024
|
-
|
|
3025
|
-
assertType<
|
|
3026
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3027
|
-
? true
|
|
3028
|
-
: false,
|
|
3029
|
-
true
|
|
3030
|
-
>();
|
|
3243
|
+
});
|
|
3031
3244
|
});
|
|
3032
3245
|
});
|
|
3033
3246
|
|
|
@@ -3114,6 +3327,11 @@ describe("createType", () => {
|
|
|
3114
3327
|
NonEmptyString.formatError({ type: "NonEmptyString", value: "" }),
|
|
3115
3328
|
"Enter some text.",
|
|
3116
3329
|
);
|
|
3330
|
+
assertAssertionError(
|
|
3331
|
+
() => assertType(NonEmptyString, ""),
|
|
3332
|
+
"Expected NonEmptyString.",
|
|
3333
|
+
{ type: "NonEmptyString", value: "" },
|
|
3334
|
+
);
|
|
3117
3335
|
});
|
|
3118
3336
|
|
|
3119
3337
|
it("asserts that validation callbacks preserve identity", () => {
|
|
@@ -3173,7 +3391,7 @@ describe("createType", () => {
|
|
|
3173
3391
|
assertEqual(FortyTwo.from(42), ok(42));
|
|
3174
3392
|
assertType<typeof FortyTwo.Output, 42>();
|
|
3175
3393
|
|
|
3176
|
-
|
|
3394
|
+
void (() => {
|
|
3177
3395
|
createType(
|
|
3178
3396
|
"Length",
|
|
3179
3397
|
String,
|
|
@@ -3197,14 +3415,7 @@ describe("createType", () => {
|
|
|
3197
3415
|
// @ts-expect-error An infallible child Type must not provide a formatter.
|
|
3198
3416
|
() => "Unreachable.",
|
|
3199
3417
|
);
|
|
3200
|
-
};
|
|
3201
|
-
|
|
3202
|
-
assertType<
|
|
3203
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3204
|
-
? true
|
|
3205
|
-
: false,
|
|
3206
|
-
true
|
|
3207
|
-
>();
|
|
3418
|
+
});
|
|
3208
3419
|
});
|
|
3209
3420
|
|
|
3210
3421
|
it("requires a formatter for root and fallible child Types", () => {
|
|
@@ -3217,21 +3428,14 @@ describe("createType", () => {
|
|
|
3217
3428
|
): Result<never, ChildWithoutFormatterError> =>
|
|
3218
3429
|
err({ type: "ChildWithoutFormatter", value });
|
|
3219
3430
|
|
|
3220
|
-
|
|
3431
|
+
void (() => {
|
|
3221
3432
|
// @ts-expect-error A root Type must format its validation errors.
|
|
3222
3433
|
createType("RootWithoutFormatter", (_value): Result<never, TypeError> =>
|
|
3223
3434
|
err({ type: "RootWithoutFormatter" }),
|
|
3224
3435
|
);
|
|
3225
3436
|
// @ts-expect-error A fallible child Type must format its own error.
|
|
3226
3437
|
createType("ChildWithoutFormatter", String, fromChildWithoutFormatter);
|
|
3227
|
-
};
|
|
3228
|
-
|
|
3229
|
-
assertType<
|
|
3230
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3231
|
-
? true
|
|
3232
|
-
: false,
|
|
3233
|
-
true
|
|
3234
|
-
>();
|
|
3438
|
+
});
|
|
3235
3439
|
});
|
|
3236
3440
|
|
|
3237
3441
|
it("inherits the parent formatter when a child is infallible", () => {
|
|
@@ -3283,7 +3487,7 @@ describe("createType", () => {
|
|
|
3283
3487
|
readonly value: string;
|
|
3284
3488
|
}
|
|
3285
3489
|
|
|
3286
|
-
|
|
3490
|
+
void (() => {
|
|
3287
3491
|
createType(
|
|
3288
3492
|
"TypeOf",
|
|
3289
3493
|
// @ts-expect-error A child error type must not duplicate an inherited error type.
|
|
@@ -3292,14 +3496,7 @@ describe("createType", () => {
|
|
|
3292
3496
|
err({ type: "TypeOf", value }),
|
|
3293
3497
|
() => "Duplicate TypeOf error.",
|
|
3294
3498
|
);
|
|
3295
|
-
};
|
|
3296
|
-
|
|
3297
|
-
assertType<
|
|
3298
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3299
|
-
? true
|
|
3300
|
-
: false,
|
|
3301
|
-
true
|
|
3302
|
-
>();
|
|
3499
|
+
});
|
|
3303
3500
|
});
|
|
3304
3501
|
|
|
3305
3502
|
it("requires one concrete name for a fallible child Type", () => {
|
|
@@ -3314,37 +3511,28 @@ describe("createType", () => {
|
|
|
3314
3511
|
err({ type: "A", value });
|
|
3315
3512
|
const formatError = () => "A error.";
|
|
3316
3513
|
|
|
3317
|
-
|
|
3514
|
+
void (() => {
|
|
3318
3515
|
// @ts-expect-error A union does not identify one concrete Type name.
|
|
3319
3516
|
createType(unionName, String, from, formatError);
|
|
3320
3517
|
// @ts-expect-error A widened string does not identify one concrete Type name.
|
|
3321
3518
|
createType(broadName, String, from, formatError);
|
|
3322
3519
|
// @ts-expect-error A template pattern does not identify one concrete Type name.
|
|
3323
3520
|
createType(patternedName, String, from, formatError);
|
|
3324
|
-
};
|
|
3325
|
-
|
|
3326
|
-
assertType<
|
|
3327
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3328
|
-
? true
|
|
3329
|
-
: false,
|
|
3330
|
-
true
|
|
3331
|
-
>();
|
|
3521
|
+
});
|
|
3332
3522
|
});
|
|
3333
3523
|
|
|
3334
3524
|
it("requires one concrete name for root and infallible child Types", () => {
|
|
3335
3525
|
const unionName = "A" as "A" | "B";
|
|
3336
3526
|
const broadName = "A" as Capitalize<string>;
|
|
3337
3527
|
const patternedName = "A" as `A${string}`;
|
|
3338
|
-
|
|
3339
|
-
name: Name,
|
|
3340
|
-
): Name => {
|
|
3528
|
+
void (<Name extends "A" | "B">(name: Name): Name => {
|
|
3341
3529
|
// @ts-expect-error An unresolved generic root name might be a union.
|
|
3342
3530
|
createType(name, ok, () => "Generic root error.");
|
|
3343
3531
|
// @ts-expect-error An unresolved generic child name might be a union.
|
|
3344
3532
|
createType(name, String, ok);
|
|
3345
3533
|
return name;
|
|
3346
|
-
};
|
|
3347
|
-
|
|
3534
|
+
});
|
|
3535
|
+
void (() => {
|
|
3348
3536
|
// @ts-expect-error A union does not identify one concrete root Type name.
|
|
3349
3537
|
createType(unionName, ok, () => "Root error.");
|
|
3350
3538
|
// @ts-expect-error A widened string does not identify one concrete root Type name.
|
|
@@ -3357,30 +3545,13 @@ describe("createType", () => {
|
|
|
3357
3545
|
createType(broadName, String, ok);
|
|
3358
3546
|
// @ts-expect-error A template pattern does not identify one concrete child Type name.
|
|
3359
3547
|
createType(patternedName, String, ok);
|
|
3360
|
-
};
|
|
3361
|
-
|
|
3362
|
-
assertType<
|
|
3363
|
-
typeof genericCompileTimeAssertion extends (
|
|
3364
|
-
...args: Array<never>
|
|
3365
|
-
) => unknown
|
|
3366
|
-
? true
|
|
3367
|
-
: false,
|
|
3368
|
-
true
|
|
3369
|
-
>();
|
|
3370
|
-
assertType<
|
|
3371
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3372
|
-
? true
|
|
3373
|
-
: false,
|
|
3374
|
-
true
|
|
3375
|
-
>();
|
|
3548
|
+
});
|
|
3376
3549
|
});
|
|
3377
3550
|
|
|
3378
3551
|
it("rejects a parent with erased concrete Type information", () => {
|
|
3379
3552
|
const erased: FormattableTypeNode = brand("ErasedParent", String);
|
|
3380
3553
|
|
|
3381
|
-
|
|
3382
|
-
Parent extends typeof String | typeof Number,
|
|
3383
|
-
>(
|
|
3554
|
+
void (<Parent extends typeof String | typeof Number>(
|
|
3384
3555
|
parent: Parent,
|
|
3385
3556
|
): Parent => {
|
|
3386
3557
|
// @ts-expect-error A parent must preserve its concrete Type.
|
|
@@ -3388,14 +3559,7 @@ describe("createType", () => {
|
|
|
3388
3559
|
// @ts-expect-error An unresolved generic parent might be a union.
|
|
3389
3560
|
createType("GenericChild", parent, ok);
|
|
3390
3561
|
return parent;
|
|
3391
|
-
};
|
|
3392
|
-
|
|
3393
|
-
assertType<
|
|
3394
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3395
|
-
? true
|
|
3396
|
-
: false,
|
|
3397
|
-
true
|
|
3398
|
-
>();
|
|
3562
|
+
});
|
|
3399
3563
|
});
|
|
3400
3564
|
|
|
3401
3565
|
it("requires a child error type matching the child name", () => {
|
|
@@ -3403,7 +3567,7 @@ describe("createType", () => {
|
|
|
3403
3567
|
readonly value: string;
|
|
3404
3568
|
}
|
|
3405
3569
|
|
|
3406
|
-
|
|
3570
|
+
void (() => {
|
|
3407
3571
|
createType(
|
|
3408
3572
|
"ChildName",
|
|
3409
3573
|
String,
|
|
@@ -3412,14 +3576,7 @@ describe("createType", () => {
|
|
|
3412
3576
|
err({ type: "DifferentName", value }),
|
|
3413
3577
|
() => "Different name error.",
|
|
3414
3578
|
);
|
|
3415
|
-
};
|
|
3416
|
-
|
|
3417
|
-
assertType<
|
|
3418
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
3419
|
-
? true
|
|
3420
|
-
: false,
|
|
3421
|
-
true
|
|
3422
|
-
>();
|
|
3579
|
+
});
|
|
3423
3580
|
});
|
|
3424
3581
|
|
|
3425
3582
|
describe("Type", () => {
|
|
@@ -3691,10 +3848,41 @@ describe("createTypeWithError", () => {
|
|
|
3691
3848
|
]);
|
|
3692
3849
|
});
|
|
3693
3850
|
|
|
3851
|
+
it("maps only invalid values inside structural Types", () => {
|
|
3852
|
+
const mapError = mock.fn((cause: InferErrors<typeof String>) => ({
|
|
3853
|
+
type: "Value" as const,
|
|
3854
|
+
cause,
|
|
3855
|
+
}));
|
|
3856
|
+
const Values = array(
|
|
3857
|
+
createTypeWithError("Value", String, mapError, () => "Invalid value."),
|
|
3858
|
+
);
|
|
3859
|
+
const valid = ["a", "b"];
|
|
3860
|
+
|
|
3861
|
+
assertOk(Values.fromUnknown(valid), valid);
|
|
3862
|
+
assertSame(mapError.mock.callCount(), 0);
|
|
3863
|
+
assertEqual(Values.fromUnknown(["a", 1]), {
|
|
3864
|
+
ok: false,
|
|
3865
|
+
error: {
|
|
3866
|
+
type: "Array",
|
|
3867
|
+
reason: {
|
|
3868
|
+
kind: "Items",
|
|
3869
|
+
issues: [
|
|
3870
|
+
{
|
|
3871
|
+
kind: "Element",
|
|
3872
|
+
index: 1,
|
|
3873
|
+
error: {
|
|
3874
|
+
type: "Value",
|
|
3875
|
+
cause: { type: "TypeOf", expected: "String", value: 1 },
|
|
3876
|
+
},
|
|
3877
|
+
},
|
|
3878
|
+
],
|
|
3879
|
+
},
|
|
3880
|
+
},
|
|
3881
|
+
});
|
|
3882
|
+
});
|
|
3883
|
+
|
|
3694
3884
|
it("rejects transforming sources and incompatible error names", () => {
|
|
3695
|
-
|
|
3696
|
-
uncertain: typeof String | typeof Number,
|
|
3697
|
-
) => {
|
|
3885
|
+
void ((uncertain: typeof String | typeof Number) => {
|
|
3698
3886
|
createTypeWithError(
|
|
3699
3887
|
"Value",
|
|
3700
3888
|
// @ts-expect-error Source Type must use identity encoding.
|
|
@@ -3716,11 +3904,7 @@ describe("createTypeWithError", () => {
|
|
|
3716
3904
|
() => ({ type: "Other" as const }),
|
|
3717
3905
|
() => "Invalid value.",
|
|
3718
3906
|
);
|
|
3719
|
-
};
|
|
3720
|
-
assertType<
|
|
3721
|
-
typeof compileTimeAssertions,
|
|
3722
|
-
(uncertain: typeof String | typeof Number) => void
|
|
3723
|
-
>();
|
|
3907
|
+
});
|
|
3724
3908
|
});
|
|
3725
3909
|
});
|
|
3726
3910
|
|
|
@@ -4136,7 +4320,7 @@ describe("transform", () => {
|
|
|
4136
4320
|
});
|
|
4137
4321
|
|
|
4138
4322
|
it("requires a formatter exactly when decoding adds an own error", () => {
|
|
4139
|
-
|
|
4323
|
+
void (() => {
|
|
4140
4324
|
interface MissingFormatterError extends TypeError<"MissingFormatter"> {
|
|
4141
4325
|
readonly value: string;
|
|
4142
4326
|
}
|
|
@@ -4158,18 +4342,11 @@ describe("transform", () => {
|
|
|
4158
4342
|
// @ts-expect-error An infallible transformation has no own error to format.
|
|
4159
4343
|
formatTestTypeError,
|
|
4160
4344
|
);
|
|
4161
|
-
};
|
|
4162
|
-
|
|
4163
|
-
assertType<
|
|
4164
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4165
|
-
? true
|
|
4166
|
-
: false,
|
|
4167
|
-
true
|
|
4168
|
-
>();
|
|
4345
|
+
});
|
|
4169
4346
|
});
|
|
4170
4347
|
|
|
4171
4348
|
it("requires callbacks to return their declared boundary types", () => {
|
|
4172
|
-
|
|
4349
|
+
void (() => {
|
|
4173
4350
|
transform("InvalidOutput", String, Number, {
|
|
4174
4351
|
// @ts-expect-error A callback must return the output Type Input.
|
|
4175
4352
|
from: () => ok("not a number"),
|
|
@@ -4180,14 +4357,7 @@ describe("transform", () => {
|
|
|
4180
4357
|
// @ts-expect-error An encoder must return the parent Type Output.
|
|
4181
4358
|
to: () => 42,
|
|
4182
4359
|
});
|
|
4183
|
-
};
|
|
4184
|
-
|
|
4185
|
-
assertType<
|
|
4186
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4187
|
-
? true
|
|
4188
|
-
: false,
|
|
4189
|
-
true
|
|
4190
|
-
>();
|
|
4360
|
+
});
|
|
4191
4361
|
});
|
|
4192
4362
|
|
|
4193
4363
|
it("asserts successful decoding callback results", () => {
|
|
@@ -4267,8 +4437,10 @@ describe("transform", () => {
|
|
|
4267
4437
|
);
|
|
4268
4438
|
const erased: FormattableTypeNode = PositiveNumber;
|
|
4269
4439
|
|
|
4270
|
-
|
|
4271
|
-
//
|
|
4440
|
+
void (() => {
|
|
4441
|
+
// An erased parent exposes every error type, so the name check rejects it
|
|
4442
|
+
// before the concrete Type check.
|
|
4443
|
+
// @ts-expect-error Error type must not duplicate an error inherited from the parent Type.
|
|
4272
4444
|
transform("ErasedParent", erased, Number, {
|
|
4273
4445
|
from: () => ok(42),
|
|
4274
4446
|
to: () => 42,
|
|
@@ -4278,28 +4450,21 @@ describe("transform", () => {
|
|
|
4278
4450
|
from: () => ok(-1),
|
|
4279
4451
|
to: globalThis.String,
|
|
4280
4452
|
});
|
|
4281
|
-
};
|
|
4282
|
-
|
|
4283
|
-
assertType<
|
|
4284
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4285
|
-
? true
|
|
4286
|
-
: false,
|
|
4287
|
-
true
|
|
4288
|
-
>();
|
|
4453
|
+
});
|
|
4289
4454
|
});
|
|
4290
4455
|
|
|
4291
4456
|
it("rejects unresolved generic parent and output Types", () => {
|
|
4292
4457
|
const _A = literal("a");
|
|
4293
4458
|
const _B = literal("b");
|
|
4294
4459
|
|
|
4295
|
-
|
|
4460
|
+
void (<
|
|
4296
4461
|
Parent extends typeof String | typeof Number,
|
|
4297
4462
|
Output extends typeof _A | typeof _B,
|
|
4298
4463
|
>(
|
|
4299
4464
|
parent: Parent,
|
|
4300
4465
|
output: Output,
|
|
4301
4466
|
): readonly [Parent, Output] => {
|
|
4302
|
-
// @ts-expect-error
|
|
4467
|
+
// @ts-expect-error The name cannot be checked against an unresolved generic parent.
|
|
4303
4468
|
transform("GenericParent", parent, Number, {
|
|
4304
4469
|
from: () => ok(1),
|
|
4305
4470
|
to: () => "1",
|
|
@@ -4310,14 +4475,24 @@ describe("transform", () => {
|
|
|
4310
4475
|
to: (value) => value,
|
|
4311
4476
|
});
|
|
4312
4477
|
return [parent, output];
|
|
4313
|
-
};
|
|
4478
|
+
});
|
|
4479
|
+
});
|
|
4314
4480
|
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4481
|
+
it("rejects a union of parent Types", () => {
|
|
4482
|
+
const parent = String as typeof String | typeof TrimmedString;
|
|
4483
|
+
|
|
4484
|
+
void (() => {
|
|
4485
|
+
transform(
|
|
4486
|
+
"UnionParent",
|
|
4487
|
+
// @ts-expect-error Parent must be one concrete Type node. Pass a Union Type node instead of a union of Type nodes.
|
|
4488
|
+
parent,
|
|
4489
|
+
Number,
|
|
4490
|
+
{
|
|
4491
|
+
from: () => ok(1),
|
|
4492
|
+
to: () => "1",
|
|
4493
|
+
},
|
|
4494
|
+
);
|
|
4495
|
+
});
|
|
4321
4496
|
});
|
|
4322
4497
|
|
|
4323
4498
|
it("requires an own error to use the transformation name", () => {
|
|
@@ -4325,7 +4500,7 @@ describe("transform", () => {
|
|
|
4325
4500
|
readonly value: string;
|
|
4326
4501
|
}
|
|
4327
4502
|
|
|
4328
|
-
|
|
4503
|
+
void (() => {
|
|
4329
4504
|
transform(
|
|
4330
4505
|
"NumberFromString",
|
|
4331
4506
|
String,
|
|
@@ -4338,14 +4513,7 @@ describe("transform", () => {
|
|
|
4338
4513
|
},
|
|
4339
4514
|
formatTestTypeError,
|
|
4340
4515
|
);
|
|
4341
|
-
};
|
|
4342
|
-
|
|
4343
|
-
assertType<
|
|
4344
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4345
|
-
? true
|
|
4346
|
-
: false,
|
|
4347
|
-
true
|
|
4348
|
-
>();
|
|
4516
|
+
});
|
|
4349
4517
|
});
|
|
4350
4518
|
|
|
4351
4519
|
it("reserves outputError for errors from the output Type", () => {
|
|
@@ -4353,7 +4521,7 @@ describe("transform", () => {
|
|
|
4353
4521
|
readonly outputError: TypeOfError<"Number">;
|
|
4354
4522
|
}
|
|
4355
4523
|
|
|
4356
|
-
|
|
4524
|
+
void (() => {
|
|
4357
4525
|
transform(
|
|
4358
4526
|
"NumberFromString",
|
|
4359
4527
|
String,
|
|
@@ -4373,14 +4541,7 @@ describe("transform", () => {
|
|
|
4373
4541
|
},
|
|
4374
4542
|
formatTestTypeError,
|
|
4375
4543
|
);
|
|
4376
|
-
};
|
|
4377
|
-
|
|
4378
|
-
assertType<
|
|
4379
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4380
|
-
? true
|
|
4381
|
-
: false,
|
|
4382
|
-
true
|
|
4383
|
-
>();
|
|
4544
|
+
});
|
|
4384
4545
|
});
|
|
4385
4546
|
|
|
4386
4547
|
it("does not widen its own errors from a broad formatter", () => {
|
|
@@ -4587,10 +4748,10 @@ describe("objectTag", () => {
|
|
|
4587
4748
|
const fromParentResult = Tagged.from.parent(value);
|
|
4588
4749
|
const transformedResult = TaggedFromString.fromUnknown("Tagged");
|
|
4589
4750
|
const wrongTag = new TaggedValue("Other");
|
|
4590
|
-
|
|
4751
|
+
void (() => {
|
|
4591
4752
|
// @ts-expect-error A raw parent Output has no validated object-tag evidence.
|
|
4592
4753
|
Tagged.to(value);
|
|
4593
|
-
};
|
|
4754
|
+
});
|
|
4594
4755
|
|
|
4595
4756
|
assertType<
|
|
4596
4757
|
typeof Tagged,
|
|
@@ -4636,12 +4797,6 @@ describe("objectTag", () => {
|
|
|
4636
4797
|
}),
|
|
4637
4798
|
'A value {"tag":"Other"} does not have the expected object tag "Tagged".',
|
|
4638
4799
|
);
|
|
4639
|
-
assertType<
|
|
4640
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4641
|
-
? true
|
|
4642
|
-
: false,
|
|
4643
|
-
true
|
|
4644
|
-
>();
|
|
4645
4800
|
});
|
|
4646
4801
|
|
|
4647
4802
|
it("requires one concrete tag name and a Type for custom tags", () => {
|
|
@@ -4661,14 +4816,12 @@ describe("objectTag", () => {
|
|
|
4661
4816
|
const outputType = TaggedValueInstance as
|
|
4662
4817
|
typeof TaggedValueInstance | typeof _OtherValueInstance;
|
|
4663
4818
|
const erasedOutputType: TypeNode = TaggedValueInstance;
|
|
4664
|
-
|
|
4665
|
-
name: Name,
|
|
4666
|
-
): Name => {
|
|
4819
|
+
void (<Name extends "Tagged" | "Other">(name: Name): Name => {
|
|
4667
4820
|
// @ts-expect-error An unresolved generic could be instantiated with a tag union.
|
|
4668
4821
|
objectTag(name, TaggedValueInstance);
|
|
4669
4822
|
return name;
|
|
4670
|
-
};
|
|
4671
|
-
|
|
4823
|
+
});
|
|
4824
|
+
void (<
|
|
4672
4825
|
OutputType extends
|
|
4673
4826
|
typeof TaggedValueInstance | typeof _OtherValueInstance,
|
|
4674
4827
|
>(
|
|
@@ -4677,8 +4830,8 @@ describe("objectTag", () => {
|
|
|
4677
4830
|
// @ts-expect-error An unresolved generic could be instantiated with an output Type union.
|
|
4678
4831
|
objectTag("Tagged", outputType);
|
|
4679
4832
|
return outputType;
|
|
4680
|
-
};
|
|
4681
|
-
|
|
4833
|
+
});
|
|
4834
|
+
void (() => {
|
|
4682
4835
|
// @ts-expect-error A union does not identify one concrete object tag.
|
|
4683
4836
|
objectTag(unionName, TaggedValueInstance);
|
|
4684
4837
|
// @ts-expect-error A union does not identify one predefined object tag.
|
|
@@ -4695,26 +4848,7 @@ describe("objectTag", () => {
|
|
|
4695
4848
|
objectTag("Tagged", outputType);
|
|
4696
4849
|
// @ts-expect-error The output Type must retain its concrete information.
|
|
4697
4850
|
objectTag("Tagged", erasedOutputType);
|
|
4698
|
-
};
|
|
4699
|
-
|
|
4700
|
-
assertType<
|
|
4701
|
-
typeof genericNameAssertion extends (...args: Array<never>) => unknown
|
|
4702
|
-
? true
|
|
4703
|
-
: false,
|
|
4704
|
-
true
|
|
4705
|
-
>();
|
|
4706
|
-
assertType<
|
|
4707
|
-
typeof genericOutputAssertion extends (...args: Array<never>) => unknown
|
|
4708
|
-
? true
|
|
4709
|
-
: false,
|
|
4710
|
-
true
|
|
4711
|
-
>();
|
|
4712
|
-
assertType<
|
|
4713
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4714
|
-
? true
|
|
4715
|
-
: false,
|
|
4716
|
-
true
|
|
4717
|
-
>();
|
|
4851
|
+
});
|
|
4718
4852
|
});
|
|
4719
4853
|
|
|
4720
4854
|
it("rejects an ObjectTag error inherited from the output Type", () => {
|
|
@@ -4723,20 +4857,13 @@ describe("objectTag", () => {
|
|
|
4723
4857
|
}
|
|
4724
4858
|
|
|
4725
4859
|
const Tagged = objectTag("Tagged", instanceOf(TaggedValue));
|
|
4726
|
-
|
|
4860
|
+
void (() => {
|
|
4727
4861
|
objectTag(
|
|
4728
4862
|
"Other",
|
|
4729
4863
|
// @ts-expect-error An ObjectTag error must not duplicate one inherited from the output Type.
|
|
4730
4864
|
Tagged,
|
|
4731
4865
|
);
|
|
4732
|
-
};
|
|
4733
|
-
|
|
4734
|
-
assertType<
|
|
4735
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4736
|
-
? true
|
|
4737
|
-
: false,
|
|
4738
|
-
true
|
|
4739
|
-
>();
|
|
4866
|
+
});
|
|
4740
4867
|
});
|
|
4741
4868
|
|
|
4742
4869
|
it("infers ObjectTag localization", () => {
|
|
@@ -4808,22 +4935,17 @@ describe("instanceOf", () => {
|
|
|
4808
4935
|
type ErasedConstructorParameter = Parameters<
|
|
4809
4936
|
typeof instanceOf<ErasedConstructor>
|
|
4810
4937
|
>[0];
|
|
4811
|
-
|
|
4812
|
-
constructor: Constructor,
|
|
4813
|
-
erasedConstructor: ErasedConstructor,
|
|
4814
|
-
) => {
|
|
4938
|
+
void ((constructor: Constructor, erasedConstructor: ErasedConstructor) => {
|
|
4815
4939
|
// @ts-expect-error An Instance Type requires one concrete constructor.
|
|
4816
4940
|
instanceOf(constructor);
|
|
4817
4941
|
// @ts-expect-error An Instance Type requires concrete constructor information.
|
|
4818
4942
|
instanceOf(erasedConstructor);
|
|
4819
|
-
};
|
|
4820
|
-
|
|
4821
|
-
constructor: C,
|
|
4822
|
-
): C => {
|
|
4943
|
+
});
|
|
4944
|
+
void (<C extends Constructor>(constructor: C): C => {
|
|
4823
4945
|
// @ts-expect-error An unresolved generic could be instantiated with a constructor union.
|
|
4824
4946
|
instanceOf(constructor);
|
|
4825
4947
|
return constructor;
|
|
4826
|
-
};
|
|
4948
|
+
});
|
|
4827
4949
|
|
|
4828
4950
|
assertType<
|
|
4829
4951
|
Constructor extends ConstructorParameter ? true : false,
|
|
@@ -4838,20 +4960,6 @@ describe("instanceOf", () => {
|
|
|
4838
4960
|
"⛔ Type error: Constructor must preserve one concrete constructor. Create a Union Type from separate Instance Types instead of passing a union or erased constructor."
|
|
4839
4961
|
>();
|
|
4840
4962
|
assertType<ErasedConstructorParameter, ConstructorParameter>();
|
|
4841
|
-
assertType<
|
|
4842
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
4843
|
-
? true
|
|
4844
|
-
: false,
|
|
4845
|
-
true
|
|
4846
|
-
>();
|
|
4847
|
-
assertType<
|
|
4848
|
-
typeof genericCompileTimeAssertion extends (
|
|
4849
|
-
...args: Array<never>
|
|
4850
|
-
) => unknown
|
|
4851
|
-
? true
|
|
4852
|
-
: false,
|
|
4853
|
-
true
|
|
4854
|
-
>();
|
|
4855
4963
|
});
|
|
4856
4964
|
|
|
4857
4965
|
it("composes multiple constructors through one Union Type", () => {
|
|
@@ -4973,11 +5081,10 @@ it("ValidateOutput accepts concrete Types and rejects unions of Type nodes", ()
|
|
|
4973
5081
|
assertType<ValidateOutput<typeof String>, typeof String>();
|
|
4974
5082
|
assertType<ValidateOutput<typeof Value>, typeof Value>();
|
|
4975
5083
|
const uncertain = String as typeof String | typeof Number;
|
|
4976
|
-
|
|
5084
|
+
void (() => {
|
|
4977
5085
|
// @ts-expect-error Output Type must be one concrete Type node. Pass a Union Type node instead of a union of Type nodes.
|
|
4978
5086
|
defineOutput(uncertain);
|
|
4979
|
-
};
|
|
4980
|
-
assertType<typeof reject, () => void>();
|
|
5087
|
+
});
|
|
4981
5088
|
});
|
|
4982
5089
|
|
|
4983
5090
|
describe("literal", () => {
|
|
@@ -4993,7 +5100,7 @@ describe("literal", () => {
|
|
|
4993
5100
|
prefix: Prefix & ValidateLiteral<Prefix>,
|
|
4994
5101
|
): Prefix => prefix;
|
|
4995
5102
|
assertEqual(definePrefix("APP_"), "APP_");
|
|
4996
|
-
|
|
5103
|
+
void (() => {
|
|
4997
5104
|
// @ts-expect-error Expected must be one concrete literal value.
|
|
4998
5105
|
definePrefix("APP_" as string);
|
|
4999
5106
|
// @ts-expect-error Expected must be one concrete literal value.
|
|
@@ -5002,8 +5109,7 @@ describe("literal", () => {
|
|
|
5002
5109
|
definePrefix(TrimmedString.orThrow("APP_"));
|
|
5003
5110
|
// @ts-expect-error Expected must be one concrete literal value.
|
|
5004
5111
|
definePrefix("APP_" as `APP_${string}`);
|
|
5005
|
-
};
|
|
5006
|
-
assertType<typeof reject, () => void>();
|
|
5112
|
+
});
|
|
5007
5113
|
});
|
|
5008
5114
|
|
|
5009
5115
|
const Hello = literal("Hello");
|
|
@@ -5152,7 +5258,7 @@ describe("literal", () => {
|
|
|
5152
5258
|
const Amount = brand("Amount", Number);
|
|
5153
5259
|
const amount = getOrThrow(Amount.from.parent(42));
|
|
5154
5260
|
|
|
5155
|
-
|
|
5261
|
+
void (() => {
|
|
5156
5262
|
// @ts-expect-error A widened string does not identify one literal.
|
|
5157
5263
|
literal(stringValue);
|
|
5158
5264
|
// @ts-expect-error A widened number does not identify one literal.
|
|
@@ -5173,13 +5279,7 @@ describe("literal", () => {
|
|
|
5173
5279
|
literal(globalThis.Number.POSITIVE_INFINITY);
|
|
5174
5280
|
// @ts-expect-error NaN has the widened `number` type.
|
|
5175
5281
|
literal(globalThis.Number.NaN);
|
|
5176
|
-
};
|
|
5177
|
-
assertType<
|
|
5178
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
5179
|
-
? true
|
|
5180
|
-
: false,
|
|
5181
|
-
true
|
|
5182
|
-
>();
|
|
5282
|
+
});
|
|
5183
5283
|
});
|
|
5184
5284
|
|
|
5185
5285
|
describe("Type", () => {
|
|
@@ -5831,7 +5931,7 @@ describe("union", () => {
|
|
|
5831
5931
|
const uncertain = String as typeof String | typeof Number;
|
|
5832
5932
|
const possiblyInfallible = String as typeof String | typeof Unknown;
|
|
5833
5933
|
const erased: FormattableTypeNode = String;
|
|
5834
|
-
|
|
5934
|
+
void (() => {
|
|
5835
5935
|
// @ts-expect-error A member must use one concrete Type node.
|
|
5836
5936
|
union(uncertain, Boolean).to(42);
|
|
5837
5937
|
// @ts-expect-error A member must preserve its concrete Type information.
|
|
@@ -5842,14 +5942,7 @@ describe("union", () => {
|
|
|
5842
5942
|
union("draft", erased).to(true);
|
|
5843
5943
|
// @ts-expect-error Infallibility must not depend on a runtime member choice.
|
|
5844
5944
|
union(possiblyInfallible, Number).to(true);
|
|
5845
|
-
};
|
|
5846
|
-
|
|
5847
|
-
assertType<
|
|
5848
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
5849
|
-
? true
|
|
5850
|
-
: false,
|
|
5851
|
-
true
|
|
5852
|
-
>();
|
|
5945
|
+
});
|
|
5853
5946
|
});
|
|
5854
5947
|
|
|
5855
5948
|
it("forwards all-errors mode to member Types", () => {
|
|
@@ -6253,7 +6346,7 @@ describe("union", () => {
|
|
|
6253
6346
|
const stringValue = "draft" as string;
|
|
6254
6347
|
const unionValue = "draft" as "draft" | "published";
|
|
6255
6348
|
|
|
6256
|
-
|
|
6349
|
+
void (() => {
|
|
6257
6350
|
// @ts-expect-error A Union requires at least two member Types.
|
|
6258
6351
|
union();
|
|
6259
6352
|
// @ts-expect-error A Union requires at least two member Types.
|
|
@@ -6268,14 +6361,7 @@ describe("union", () => {
|
|
|
6268
6361
|
union(Number, stringValue);
|
|
6269
6362
|
// @ts-expect-error A union does not identify one literal.
|
|
6270
6363
|
union(Number, unionValue);
|
|
6271
|
-
};
|
|
6272
|
-
|
|
6273
|
-
assertType<
|
|
6274
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
6275
|
-
? true
|
|
6276
|
-
: false,
|
|
6277
|
-
true
|
|
6278
|
-
>();
|
|
6364
|
+
});
|
|
6279
6365
|
});
|
|
6280
6366
|
});
|
|
6281
6367
|
|
|
@@ -6310,19 +6396,12 @@ describe("undefinedOr", () => {
|
|
|
6310
6396
|
type Value = typeof String | typeof Number;
|
|
6311
6397
|
const value = String as Value;
|
|
6312
6398
|
const erased: FormattableTypeNode = String;
|
|
6313
|
-
|
|
6399
|
+
void (() => {
|
|
6314
6400
|
// @ts-expect-error The supplied Type must use one concrete Type node.
|
|
6315
6401
|
undefinedOr(value);
|
|
6316
6402
|
// @ts-expect-error The supplied Type must preserve its concrete information.
|
|
6317
6403
|
undefinedOr(erased);
|
|
6318
|
-
};
|
|
6319
|
-
|
|
6320
|
-
assertType<
|
|
6321
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
6322
|
-
? true
|
|
6323
|
-
: false,
|
|
6324
|
-
true
|
|
6325
|
-
>();
|
|
6404
|
+
});
|
|
6326
6405
|
});
|
|
6327
6406
|
});
|
|
6328
6407
|
|
|
@@ -6345,19 +6424,12 @@ describe("nullOr", () => {
|
|
|
6345
6424
|
type Value = typeof String | typeof Number;
|
|
6346
6425
|
const value = String as Value;
|
|
6347
6426
|
const erased: FormattableTypeNode = String;
|
|
6348
|
-
|
|
6427
|
+
void (() => {
|
|
6349
6428
|
// @ts-expect-error The supplied Type must use one concrete Type node.
|
|
6350
6429
|
nullOr(value);
|
|
6351
6430
|
// @ts-expect-error The supplied Type must preserve its concrete information.
|
|
6352
6431
|
nullOr(erased);
|
|
6353
|
-
};
|
|
6354
|
-
|
|
6355
|
-
assertType<
|
|
6356
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
6357
|
-
? true
|
|
6358
|
-
: false,
|
|
6359
|
-
true
|
|
6360
|
-
>();
|
|
6432
|
+
});
|
|
6361
6433
|
});
|
|
6362
6434
|
});
|
|
6363
6435
|
|
|
@@ -6380,19 +6452,12 @@ describe("nullishOr", () => {
|
|
|
6380
6452
|
type Value = typeof String | typeof Number;
|
|
6381
6453
|
const value = String as Value;
|
|
6382
6454
|
const erased: FormattableTypeNode = String;
|
|
6383
|
-
|
|
6455
|
+
void (() => {
|
|
6384
6456
|
// @ts-expect-error The supplied Type must use one concrete Type node.
|
|
6385
6457
|
nullishOr(value);
|
|
6386
6458
|
// @ts-expect-error The supplied Type must preserve its concrete information.
|
|
6387
6459
|
nullishOr(erased);
|
|
6388
|
-
};
|
|
6389
|
-
|
|
6390
|
-
assertType<
|
|
6391
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
6392
|
-
? true
|
|
6393
|
-
: false,
|
|
6394
|
-
true
|
|
6395
|
-
>();
|
|
6460
|
+
});
|
|
6396
6461
|
});
|
|
6397
6462
|
});
|
|
6398
6463
|
|
|
@@ -6610,14 +6675,13 @@ describe("templateLiteralParser", () => {
|
|
|
6610
6675
|
const FixedWidth = union("🙂", "a");
|
|
6611
6676
|
const VariableWidth = union("🙂", "ab");
|
|
6612
6677
|
const Value = templateLiteralParser(FixedWidth, String);
|
|
6613
|
-
|
|
6678
|
+
void (() => {
|
|
6614
6679
|
// @ts-expect-error At most one Type capture can have a variable-width string representation.
|
|
6615
6680
|
templateLiteralParser(VariableWidth, String);
|
|
6616
|
-
};
|
|
6681
|
+
});
|
|
6617
6682
|
|
|
6618
6683
|
assertOk(Value.fromUnknown("🙂value"), ["🙂", "value"]);
|
|
6619
6684
|
assertOk(Value.fromUnknown("avalue"), ["a", "value"]);
|
|
6620
|
-
void compileTimeAssertions;
|
|
6621
6685
|
});
|
|
6622
6686
|
|
|
6623
6687
|
it("rejects surrogate pairs formed across part boundaries", () => {
|
|
@@ -6718,14 +6782,13 @@ describe("templateLiteralParser", () => {
|
|
|
6718
6782
|
assertFalse(NumberValue.parent.is("number:not-a-number"));
|
|
6719
6783
|
assertOk(NumberValue.from.parent(encoded), [42]);
|
|
6720
6784
|
|
|
6721
|
-
|
|
6785
|
+
void (() => {
|
|
6722
6786
|
// @ts-expect-error A matching string is not necessarily canonical.
|
|
6723
6787
|
const validLooking: NumberLiteral = "number:42";
|
|
6724
6788
|
// @ts-expect-error An invalid string is not canonical.
|
|
6725
6789
|
const invalid: NumberLiteral = "number:not-a-number";
|
|
6726
6790
|
return [validLooking, invalid];
|
|
6727
|
-
};
|
|
6728
|
-
void compileTimeAssertions;
|
|
6791
|
+
});
|
|
6729
6792
|
});
|
|
6730
6793
|
|
|
6731
6794
|
it("distinguishes transforming templates with the same string shape", () => {
|
|
@@ -6787,23 +6850,21 @@ describe("templateLiteralParser", () => {
|
|
|
6787
6850
|
it("rejects ambiguous variable-width captures statically", () => {
|
|
6788
6851
|
const VariableUnion = union("a", "bb");
|
|
6789
6852
|
const Inner = templateLiteralParser("<", String, ">");
|
|
6790
|
-
|
|
6853
|
+
void (() => {
|
|
6791
6854
|
// @ts-expect-error At most one Type capture can have a variable-width string representation.
|
|
6792
6855
|
templateLiteralParser(String, ":", String);
|
|
6793
6856
|
// @ts-expect-error At most one Type capture can have a variable-width string representation.
|
|
6794
6857
|
templateLiteralParser(VariableUnion, String);
|
|
6795
6858
|
// @ts-expect-error At most one Type capture can have a variable-width string representation.
|
|
6796
6859
|
templateLiteralParser(Inner, String);
|
|
6797
|
-
};
|
|
6798
|
-
|
|
6799
|
-
void compileTimeAssertions;
|
|
6860
|
+
});
|
|
6800
6861
|
});
|
|
6801
6862
|
|
|
6802
6863
|
it("requires a capture and canonically string-encoded concrete parts", () => {
|
|
6803
6864
|
const widened = "value" as string;
|
|
6804
6865
|
const uncertain = String as typeof String | typeof Number;
|
|
6805
6866
|
const parts: ReadonlyArray<typeof String> = [String];
|
|
6806
|
-
|
|
6867
|
+
void (() => {
|
|
6807
6868
|
// @ts-expect-error At least one part must be a Type capture.
|
|
6808
6869
|
templateLiteralParser("value");
|
|
6809
6870
|
// @ts-expect-error Part must be a raw string literal or a Type canonically encoded as a string.
|
|
@@ -6818,9 +6879,7 @@ describe("templateLiteralParser", () => {
|
|
|
6818
6879
|
templateLiteralParser(uncertain);
|
|
6819
6880
|
// @ts-expect-error Parts must use one concrete finite non-empty tuple.
|
|
6820
6881
|
templateLiteralParser(...parts);
|
|
6821
|
-
};
|
|
6822
|
-
|
|
6823
|
-
void compileTimeAssertions;
|
|
6882
|
+
});
|
|
6824
6883
|
});
|
|
6825
6884
|
|
|
6826
6885
|
it("decodes every capture exactly once", () => {
|
|
@@ -7073,13 +7132,12 @@ describe("templateLiteralParser", () => {
|
|
|
7073
7132
|
);
|
|
7074
7133
|
assertEqual(SupportedLocale.to(["en", "US"]), "en-US");
|
|
7075
7134
|
|
|
7076
|
-
|
|
7135
|
+
void (() => {
|
|
7077
7136
|
const supported: SupportedLocaleLiteral = "cs-CZ";
|
|
7078
7137
|
// @ts-expect-error Unsupported language.
|
|
7079
7138
|
const unsupported: SupportedLocaleLiteral = "fr-CZ";
|
|
7080
7139
|
return [supported, unsupported];
|
|
7081
|
-
};
|
|
7082
|
-
void compileTimeAssertions;
|
|
7140
|
+
});
|
|
7083
7141
|
});
|
|
7084
7142
|
|
|
7085
7143
|
it("normalizes broader capture input into a canonical literal", () => {
|
|
@@ -7107,11 +7165,10 @@ describe("templateLiteralParser", () => {
|
|
|
7107
7165
|
);
|
|
7108
7166
|
assertEqual(Tag.to([lowercase]), "tag:evolu");
|
|
7109
7167
|
|
|
7110
|
-
|
|
7168
|
+
void (() => {
|
|
7111
7169
|
// @ts-expect-error Encoding requires a lowercase branded Output.
|
|
7112
7170
|
Tag.to(["EVOLU"]);
|
|
7113
|
-
};
|
|
7114
|
-
void compileTimeAssertions;
|
|
7171
|
+
});
|
|
7115
7172
|
});
|
|
7116
7173
|
|
|
7117
7174
|
it("exposes canonical and parsed Standard Schema stages", async () => {
|
|
@@ -7196,14 +7253,13 @@ describe("templateLiteral", () => {
|
|
|
7196
7253
|
it("uses the same reversible framing rules as templateLiteralParser", () => {
|
|
7197
7254
|
const Value = templateLiteral(String, "::");
|
|
7198
7255
|
const Parser = templateLiteralParser(String, "::");
|
|
7199
|
-
|
|
7256
|
+
void (() => {
|
|
7200
7257
|
// @ts-expect-error At most one Type capture can have a variable-width string representation.
|
|
7201
7258
|
templateLiteral(String, ":", String);
|
|
7202
|
-
};
|
|
7259
|
+
});
|
|
7203
7260
|
|
|
7204
7261
|
assertOk(Value.fromUnknown("a::::"), "a::::");
|
|
7205
7262
|
assertSame(Value.is("a::::"), Parser.parent.is("a::::"));
|
|
7206
|
-
void compileTimeAssertions;
|
|
7207
7263
|
});
|
|
7208
7264
|
});
|
|
7209
7265
|
|
|
@@ -7288,21 +7344,76 @@ describe("brand", () => {
|
|
|
7288
7344
|
>();
|
|
7289
7345
|
});
|
|
7290
7346
|
|
|
7347
|
+
it("refines decoded values and Outputs of a transformed base", () => {
|
|
7348
|
+
const Positive = brand(
|
|
7349
|
+
"Positive",
|
|
7350
|
+
setupNumberFromString(),
|
|
7351
|
+
(value) => (value > 0 ? ok() : err({ type: "Positive", value })),
|
|
7352
|
+
formatTestTypeError,
|
|
7353
|
+
);
|
|
7354
|
+
const Small = brand(
|
|
7355
|
+
"Small",
|
|
7356
|
+
Positive,
|
|
7357
|
+
(value) => (value < 10 ? ok() : err({ type: "Small", value })),
|
|
7358
|
+
formatTestTypeError,
|
|
7359
|
+
);
|
|
7360
|
+
|
|
7361
|
+
assertOk(Small.fromUnknown("5"), 5);
|
|
7362
|
+
assertEqual(Small.fromUnknown("0"), err({ type: "Positive", value: 0 }));
|
|
7363
|
+
assertEqual(Small.fromUnknown("50"), err({ type: "Small", value: 50 }));
|
|
7364
|
+
assertTrue(Small.is(5));
|
|
7365
|
+
assertFalse(Small.is(50));
|
|
7366
|
+
assertAssertionError(() => assertType(Small, 50), "Expected Small.", {
|
|
7367
|
+
type: "Small",
|
|
7368
|
+
value: 50,
|
|
7369
|
+
});
|
|
7370
|
+
assertAssertionError(() => assertType(Small, "5"), "Expected Small.", {
|
|
7371
|
+
type: "NumberFromString",
|
|
7372
|
+
outputError: { type: "TypeOf", expected: "Number", value: "5" },
|
|
7373
|
+
});
|
|
7374
|
+
});
|
|
7375
|
+
|
|
7376
|
+
it("calls validators with only the value and without a receiver", () => {
|
|
7377
|
+
const receivers = new Set<unknown>();
|
|
7378
|
+
const argumentCounts = new Set<number>();
|
|
7379
|
+
const Small = brand(
|
|
7380
|
+
"Small",
|
|
7381
|
+
Number,
|
|
7382
|
+
function (this: unknown, value, ...rest: ReadonlyArray<unknown>) {
|
|
7383
|
+
receivers.add(this);
|
|
7384
|
+
argumentCounts.add(1 + rest.length);
|
|
7385
|
+
return value < 10 ? ok() : err({ type: "Small", value });
|
|
7386
|
+
},
|
|
7387
|
+
formatTestTypeError,
|
|
7388
|
+
);
|
|
7389
|
+
// A default parameter keeps its default.
|
|
7390
|
+
const SmallEven = brand(
|
|
7391
|
+
"SmallEven",
|
|
7392
|
+
Small,
|
|
7393
|
+
(value, divisor = 2) =>
|
|
7394
|
+
value % divisor === 0 ? ok() : err({ type: "SmallEven", value }),
|
|
7395
|
+
formatTestTypeError,
|
|
7396
|
+
);
|
|
7397
|
+
|
|
7398
|
+
assertOk(Small.fromUnknown(4), 4);
|
|
7399
|
+
assertTrue(Small.is(4));
|
|
7400
|
+
assertOk(SmallEven.fromUnknown(4), 4);
|
|
7401
|
+
assertTrue(SmallEven.is(4));
|
|
7402
|
+
assertEqual(SmallEven.fromUnknown(3), err({ type: "SmallEven", value: 3 }));
|
|
7403
|
+
assertOk(array(SmallEven).fromUnknown([4]), [4]);
|
|
7404
|
+
assertOk(object({ value: SmallEven }).fromUnknown({ value: 4 }), {
|
|
7405
|
+
value: 4,
|
|
7406
|
+
});
|
|
7407
|
+
assertEqual([...receivers], [undefined]);
|
|
7408
|
+
assertEqual([...argumentCounts], [1]);
|
|
7409
|
+
});
|
|
7410
|
+
|
|
7291
7411
|
it("rejects an unresolved generic infallible name", () => {
|
|
7292
|
-
|
|
7293
|
-
name: Name,
|
|
7294
|
-
): Name => {
|
|
7412
|
+
void (<Name extends "A" | "B">(name: Name): Name => {
|
|
7295
7413
|
// @ts-expect-error An unresolved generic name might be a union.
|
|
7296
7414
|
brand<Name, typeof String>(name, String);
|
|
7297
7415
|
return name;
|
|
7298
|
-
};
|
|
7299
|
-
|
|
7300
|
-
assertType<
|
|
7301
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
7302
|
-
? true
|
|
7303
|
-
: false,
|
|
7304
|
-
true
|
|
7305
|
-
>();
|
|
7416
|
+
});
|
|
7306
7417
|
});
|
|
7307
7418
|
|
|
7308
7419
|
it("rejects a union of parent Types", () => {
|
|
@@ -7321,17 +7432,10 @@ describe("brand", () => {
|
|
|
7321
7432
|
it("rejects a parent with erased concrete Type information", () => {
|
|
7322
7433
|
const erased: FormattableTypeNode = brand("Erased", String);
|
|
7323
7434
|
|
|
7324
|
-
|
|
7435
|
+
void (() => {
|
|
7325
7436
|
// @ts-expect-error A parent must preserve its concrete Type.
|
|
7326
7437
|
brand("Wrapped", erased);
|
|
7327
|
-
};
|
|
7328
|
-
|
|
7329
|
-
assertType<
|
|
7330
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
7331
|
-
? true
|
|
7332
|
-
: false,
|
|
7333
|
-
true
|
|
7334
|
-
>();
|
|
7438
|
+
});
|
|
7335
7439
|
});
|
|
7336
7440
|
|
|
7337
7441
|
it("inherits the parent formatter when validation is infallible", () => {
|
|
@@ -7351,17 +7455,10 @@ describe("brand", () => {
|
|
|
7351
7455
|
): Result<void, ValidatedWithoutFormatterError> =>
|
|
7352
7456
|
err({ type: "ValidatedWithoutFormatter", value });
|
|
7353
7457
|
|
|
7354
|
-
|
|
7458
|
+
void (() => {
|
|
7355
7459
|
// @ts-expect-error A fallible brand must format its own error.
|
|
7356
7460
|
brand("ValidatedWithoutFormatter", String, validateWithoutFormatter);
|
|
7357
|
-
};
|
|
7358
|
-
|
|
7359
|
-
assertType<
|
|
7360
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
7361
|
-
? true
|
|
7362
|
-
: false,
|
|
7363
|
-
true
|
|
7364
|
-
>();
|
|
7461
|
+
});
|
|
7365
7462
|
});
|
|
7366
7463
|
|
|
7367
7464
|
it("requires one concrete name when validation is fallible", () => {
|
|
@@ -7376,21 +7473,14 @@ describe("brand", () => {
|
|
|
7376
7473
|
err({ type: "A", value });
|
|
7377
7474
|
const formatError = () => "A error.";
|
|
7378
7475
|
|
|
7379
|
-
|
|
7476
|
+
void (() => {
|
|
7380
7477
|
// @ts-expect-error A union does not identify one concrete Brand name.
|
|
7381
7478
|
brand(unionName, String, validate, formatError);
|
|
7382
7479
|
// @ts-expect-error A widened string does not identify one concrete Brand name.
|
|
7383
7480
|
brand(broadName, String, validate, formatError);
|
|
7384
7481
|
// @ts-expect-error A template pattern does not identify one concrete Brand name.
|
|
7385
7482
|
brand(patternedName, String, validate, formatError);
|
|
7386
|
-
};
|
|
7387
|
-
|
|
7388
|
-
assertType<
|
|
7389
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
7390
|
-
? true
|
|
7391
|
-
: false,
|
|
7392
|
-
true
|
|
7393
|
-
>();
|
|
7483
|
+
});
|
|
7394
7484
|
});
|
|
7395
7485
|
|
|
7396
7486
|
it("rejects an error type inherited from the parent Type", () => {
|
|
@@ -7398,7 +7488,7 @@ describe("brand", () => {
|
|
|
7398
7488
|
readonly value: string;
|
|
7399
7489
|
}
|
|
7400
7490
|
|
|
7401
|
-
|
|
7491
|
+
void (() => {
|
|
7402
7492
|
brand(
|
|
7403
7493
|
"TypeOf",
|
|
7404
7494
|
// @ts-expect-error A Brand error must not duplicate an inherited error type.
|
|
@@ -7407,14 +7497,7 @@ describe("brand", () => {
|
|
|
7407
7497
|
err({ type: "TypeOf", value }),
|
|
7408
7498
|
() => "Duplicate TypeOf error.",
|
|
7409
7499
|
);
|
|
7410
|
-
};
|
|
7411
|
-
|
|
7412
|
-
assertType<
|
|
7413
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
7414
|
-
? true
|
|
7415
|
-
: false,
|
|
7416
|
-
true
|
|
7417
|
-
>();
|
|
7500
|
+
});
|
|
7418
7501
|
});
|
|
7419
7502
|
|
|
7420
7503
|
it("requires a brand error type matching the brand name", () => {
|
|
@@ -7422,7 +7505,7 @@ describe("brand", () => {
|
|
|
7422
7505
|
readonly value: string;
|
|
7423
7506
|
}
|
|
7424
7507
|
|
|
7425
|
-
|
|
7508
|
+
void (() => {
|
|
7426
7509
|
brand(
|
|
7427
7510
|
"BrandName",
|
|
7428
7511
|
String,
|
|
@@ -7431,18 +7514,11 @@ describe("brand", () => {
|
|
|
7431
7514
|
err({ type: "DifferentName", value }),
|
|
7432
7515
|
() => "Different name error.",
|
|
7433
7516
|
);
|
|
7434
|
-
};
|
|
7435
|
-
|
|
7436
|
-
assertType<
|
|
7437
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
7438
|
-
? true
|
|
7439
|
-
: false,
|
|
7440
|
-
true
|
|
7441
|
-
>();
|
|
7517
|
+
});
|
|
7442
7518
|
});
|
|
7443
7519
|
|
|
7444
7520
|
it("requires validation to report success without replacing the parent value", () => {
|
|
7445
|
-
|
|
7521
|
+
void (() => {
|
|
7446
7522
|
brand(
|
|
7447
7523
|
"Lowercase",
|
|
7448
7524
|
String,
|
|
@@ -7450,14 +7526,7 @@ describe("brand", () => {
|
|
|
7450
7526
|
(value) => ok(value.toLowerCase()),
|
|
7451
7527
|
formatTestTypeError,
|
|
7452
7528
|
);
|
|
7453
|
-
};
|
|
7454
|
-
|
|
7455
|
-
assertType<
|
|
7456
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
7457
|
-
? true
|
|
7458
|
-
: false,
|
|
7459
|
-
true
|
|
7460
|
-
>();
|
|
7529
|
+
});
|
|
7461
7530
|
});
|
|
7462
7531
|
|
|
7463
7532
|
it("validates only the brand after a validated Literal parent through from.parent", () => {
|
|
@@ -8508,54 +8577,34 @@ describe("BrandFactory", () => {
|
|
|
8508
8577
|
type Parent = typeof _NumberFromString | typeof _NumberFromBoolean;
|
|
8509
8578
|
type ParentParameter = Parameters<typeof positive<Parent>>[0];
|
|
8510
8579
|
|
|
8511
|
-
|
|
8580
|
+
void ((parent: Parent) => {
|
|
8512
8581
|
// @ts-expect-error A Brand Factory requires one concrete parent Type.
|
|
8513
8582
|
positive(parent);
|
|
8514
|
-
};
|
|
8583
|
+
});
|
|
8515
8584
|
|
|
8516
8585
|
assertType<Parent extends ParentParameter ? true : false, false>();
|
|
8517
|
-
assertType<
|
|
8518
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
8519
|
-
? true
|
|
8520
|
-
: false,
|
|
8521
|
-
true
|
|
8522
|
-
>();
|
|
8523
8586
|
});
|
|
8524
8587
|
|
|
8525
8588
|
it("rejects incompatible and erased parents", () => {
|
|
8526
8589
|
const { nonEmpty } = createNonEmpty();
|
|
8527
8590
|
const erased: FormattableTypeNode = String;
|
|
8528
8591
|
|
|
8529
|
-
|
|
8592
|
+
void (() => {
|
|
8530
8593
|
// @ts-expect-error The parent Output must have a length.
|
|
8531
8594
|
nonEmpty(Number);
|
|
8532
8595
|
// @ts-expect-error The parent must preserve its concrete Type information.
|
|
8533
8596
|
nonEmpty(erased);
|
|
8534
|
-
};
|
|
8535
|
-
|
|
8536
|
-
assertType<
|
|
8537
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
8538
|
-
? true
|
|
8539
|
-
: false,
|
|
8540
|
-
true
|
|
8541
|
-
>();
|
|
8597
|
+
});
|
|
8542
8598
|
});
|
|
8543
8599
|
|
|
8544
8600
|
it("rejects a parent that already exposes the factory error type", () => {
|
|
8545
8601
|
const { nonEmpty } = createNonEmpty();
|
|
8546
8602
|
const NonEmptyString = nonEmpty(String);
|
|
8547
8603
|
|
|
8548
|
-
|
|
8604
|
+
void (() => {
|
|
8549
8605
|
// @ts-expect-error A Brand Factory error must not duplicate an inherited error type.
|
|
8550
8606
|
nonEmpty(NonEmptyString);
|
|
8551
|
-
};
|
|
8552
|
-
|
|
8553
|
-
assertType<
|
|
8554
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
8555
|
-
? true
|
|
8556
|
-
: false,
|
|
8557
|
-
true
|
|
8558
|
-
>();
|
|
8607
|
+
});
|
|
8559
8608
|
});
|
|
8560
8609
|
|
|
8561
8610
|
describe("ValidateBrandFactoryNumber", () => {
|
|
@@ -8563,7 +8612,7 @@ describe("BrandFactory", () => {
|
|
|
8563
8612
|
const value = globalThis.Number(1);
|
|
8564
8613
|
const unionValue = 1 as 1 | 2;
|
|
8565
8614
|
const nonNegativeInt = NonNegativeInt.orThrow(1);
|
|
8566
|
-
|
|
8615
|
+
void (() => {
|
|
8567
8616
|
// @ts-expect-error Numeric Brand parameters must not widen to number.
|
|
8568
8617
|
minLength(1 + 1);
|
|
8569
8618
|
// @ts-expect-error Numeric Brand parameters must not be widened.
|
|
@@ -8580,14 +8629,7 @@ describe("BrandFactory", () => {
|
|
|
8580
8629
|
minLength(unionValue);
|
|
8581
8630
|
// @ts-expect-error A branded runtime number does not identify one literal.
|
|
8582
8631
|
maxLength(nonNegativeInt);
|
|
8583
|
-
};
|
|
8584
|
-
|
|
8585
|
-
assertType<
|
|
8586
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
8587
|
-
? true
|
|
8588
|
-
: false,
|
|
8589
|
-
true
|
|
8590
|
-
>();
|
|
8632
|
+
});
|
|
8591
8633
|
});
|
|
8592
8634
|
});
|
|
8593
8635
|
|
|
@@ -9221,7 +9263,7 @@ describe("BrandFactory", () => {
|
|
|
9221
9263
|
const constant = camelCaseToConstantCase(short);
|
|
9222
9264
|
assertEqual(constant, "A_B_C");
|
|
9223
9265
|
assertType<typeof constant, ConstantCaseIdentifier>();
|
|
9224
|
-
|
|
9266
|
+
void (() => {
|
|
9225
9267
|
// @ts-expect-error Requires the CamelCaseIdentifier brand.
|
|
9226
9268
|
camelCaseToPascalCase("example");
|
|
9227
9269
|
// @ts-expect-error Requires the CamelCaseIdentifier brand.
|
|
@@ -9269,8 +9311,7 @@ describe("BrandFactory", () => {
|
|
|
9269
9311
|
// @ts-expect-error Uppercase text does not prove the ConstantCaseIdentifier grammar.
|
|
9270
9312
|
const _constant: ConstantCaseIdentifier =
|
|
9271
9313
|
"HTTP PORT" as Uppercase<string>;
|
|
9272
|
-
};
|
|
9273
|
-
assertType<typeof compileTimeAssertions, () => void>();
|
|
9314
|
+
});
|
|
9274
9315
|
});
|
|
9275
9316
|
});
|
|
9276
9317
|
|
|
@@ -9416,7 +9457,7 @@ describe("BrandFactory", () => {
|
|
|
9416
9457
|
const unionPrefix = "APP_" as "APP_" | "OTHER_";
|
|
9417
9458
|
const branded = TrimmedString.orThrow("APP_");
|
|
9418
9459
|
const templatePrefix = "APP_" as `APP_${string}`;
|
|
9419
|
-
|
|
9460
|
+
void (() => {
|
|
9420
9461
|
// @ts-expect-error Expected must be one concrete literal value.
|
|
9421
9462
|
startsWith(widened);
|
|
9422
9463
|
// @ts-expect-error Expected must be one concrete literal value.
|
|
@@ -9431,16 +9472,7 @@ describe("BrandFactory", () => {
|
|
|
9431
9472
|
startsWith("APP_")(AppName);
|
|
9432
9473
|
// @ts-expect-error OTHER_ values do not carry the StartsWithAPP_ brand.
|
|
9433
9474
|
AppName.from(otherName);
|
|
9434
|
-
};
|
|
9435
|
-
|
|
9436
|
-
assertType<
|
|
9437
|
-
typeof compileTimeAssertions extends (
|
|
9438
|
-
...args: Array<never>
|
|
9439
|
-
) => unknown
|
|
9440
|
-
? true
|
|
9441
|
-
: false,
|
|
9442
|
-
true
|
|
9443
|
-
>();
|
|
9475
|
+
});
|
|
9444
9476
|
});
|
|
9445
9477
|
});
|
|
9446
9478
|
|
|
@@ -9604,7 +9636,7 @@ describe("BrandFactory", () => {
|
|
|
9604
9636
|
value === "PORT" ? ok(value) : err({ type: "LiteralRoot" }),
|
|
9605
9637
|
() => "Expected PORT.",
|
|
9606
9638
|
);
|
|
9607
|
-
|
|
9639
|
+
void (() => {
|
|
9608
9640
|
// @ts-expect-error Expected must be one concrete literal value.
|
|
9609
9641
|
prefixed(widened);
|
|
9610
9642
|
// @ts-expect-error Expected must be one concrete literal value.
|
|
@@ -9625,15 +9657,7 @@ describe("BrandFactory", () => {
|
|
|
9625
9657
|
Setting.from.parent("port:4000");
|
|
9626
9658
|
// @ts-expect-error Encoding requires a validated Port, not a plain number.
|
|
9627
9659
|
Setting.to(4000);
|
|
9628
|
-
};
|
|
9629
|
-
assertType<
|
|
9630
|
-
typeof compileTimeAssertions extends (
|
|
9631
|
-
...args: Array<never>
|
|
9632
|
-
) => unknown
|
|
9633
|
-
? true
|
|
9634
|
-
: false,
|
|
9635
|
-
true
|
|
9636
|
-
>();
|
|
9660
|
+
});
|
|
9637
9661
|
});
|
|
9638
9662
|
});
|
|
9639
9663
|
|
|
@@ -9663,19 +9687,10 @@ describe("BrandFactory", () => {
|
|
|
9663
9687
|
});
|
|
9664
9688
|
|
|
9665
9689
|
it("returns a Brand Factory that accepts the parent separately", () => {
|
|
9666
|
-
|
|
9690
|
+
void (() => {
|
|
9667
9691
|
// @ts-expect-error Parameterized Brand Factories accept their parent separately.
|
|
9668
9692
|
minLength(1, String);
|
|
9669
|
-
};
|
|
9670
|
-
|
|
9671
|
-
assertType<
|
|
9672
|
-
typeof compileTimeAssertions extends (
|
|
9673
|
-
...args: Array<never>
|
|
9674
|
-
) => unknown
|
|
9675
|
-
? true
|
|
9676
|
-
: false,
|
|
9677
|
-
true
|
|
9678
|
-
>();
|
|
9693
|
+
});
|
|
9679
9694
|
});
|
|
9680
9695
|
|
|
9681
9696
|
describe("Type", () => {
|
|
@@ -9732,19 +9747,10 @@ describe("BrandFactory", () => {
|
|
|
9732
9747
|
});
|
|
9733
9748
|
|
|
9734
9749
|
it("requires a parent value with a length", () => {
|
|
9735
|
-
|
|
9750
|
+
void (() => {
|
|
9736
9751
|
// @ts-expect-error Length constraints require a value with a length.
|
|
9737
9752
|
maxLength(1)(Number);
|
|
9738
|
-
};
|
|
9739
|
-
|
|
9740
|
-
assertType<
|
|
9741
|
-
typeof compileTimeAssertions extends (
|
|
9742
|
-
...args: Array<never>
|
|
9743
|
-
) => unknown
|
|
9744
|
-
? true
|
|
9745
|
-
: false,
|
|
9746
|
-
true
|
|
9747
|
-
>();
|
|
9753
|
+
});
|
|
9748
9754
|
});
|
|
9749
9755
|
|
|
9750
9756
|
describe("Type", () => {
|
|
@@ -9847,19 +9853,10 @@ describe("BrandFactory", () => {
|
|
|
9847
9853
|
|
|
9848
9854
|
it("requires one concrete parent Type", () => {
|
|
9849
9855
|
const parent = String as typeof String | typeof Number;
|
|
9850
|
-
|
|
9856
|
+
void (() => {
|
|
9851
9857
|
// @ts-expect-error A constraint requires one concrete parent Type.
|
|
9852
9858
|
length(1)(parent);
|
|
9853
|
-
};
|
|
9854
|
-
|
|
9855
|
-
assertType<
|
|
9856
|
-
typeof compileTimeAssertions extends (
|
|
9857
|
-
...args: Array<never>
|
|
9858
|
-
) => unknown
|
|
9859
|
-
? true
|
|
9860
|
-
: false,
|
|
9861
|
-
true
|
|
9862
|
-
>();
|
|
9859
|
+
});
|
|
9863
9860
|
});
|
|
9864
9861
|
});
|
|
9865
9862
|
|
|
@@ -9868,23 +9865,14 @@ describe("BrandFactory", () => {
|
|
|
9868
9865
|
const unionName = "Pattern" as "Pattern" | "Other";
|
|
9869
9866
|
const broadName = "Pattern" as TypeName;
|
|
9870
9867
|
const patternedName = "Pattern" as `Pattern${string}`;
|
|
9871
|
-
|
|
9868
|
+
void (() => {
|
|
9872
9869
|
// @ts-expect-error A union does not identify one concrete Regex name.
|
|
9873
9870
|
regex(unionName, /./u);
|
|
9874
9871
|
// @ts-expect-error A widened string does not identify one concrete Regex name.
|
|
9875
9872
|
regex(broadName, /./u);
|
|
9876
9873
|
// @ts-expect-error A template pattern does not identify one concrete Regex name.
|
|
9877
9874
|
regex(patternedName, /./u);
|
|
9878
|
-
};
|
|
9879
|
-
|
|
9880
|
-
assertType<
|
|
9881
|
-
typeof compileTimeAssertions extends (
|
|
9882
|
-
...args: Array<never>
|
|
9883
|
-
) => unknown
|
|
9884
|
-
? true
|
|
9885
|
-
: false,
|
|
9886
|
-
true
|
|
9887
|
-
>();
|
|
9875
|
+
});
|
|
9888
9876
|
});
|
|
9889
9877
|
|
|
9890
9878
|
it("keeps stateful matching private and returns immutable pattern data", () => {
|
|
@@ -9927,37 +9915,19 @@ describe("BrandFactory", () => {
|
|
|
9927
9915
|
assertType<typeof failure.error.source, string>();
|
|
9928
9916
|
assertType<typeof failure.error.flags, string>();
|
|
9929
9917
|
|
|
9930
|
-
|
|
9918
|
+
void (() => {
|
|
9931
9919
|
// @ts-expect-error Regex errors do not expose the live matcher.
|
|
9932
9920
|
failure.error.pattern.test = () => true; // oxlint-disable-line typescript/no-unsafe-member-access -- The unavailable matcher access is intentionally rejected above.
|
|
9933
9921
|
// @ts-expect-error Pattern data is readonly.
|
|
9934
9922
|
failure.error.source = ".*";
|
|
9935
|
-
};
|
|
9936
|
-
|
|
9937
|
-
assertType<
|
|
9938
|
-
typeof compileTimeAssertions extends (
|
|
9939
|
-
...args: Array<never>
|
|
9940
|
-
) => unknown
|
|
9941
|
-
? true
|
|
9942
|
-
: false,
|
|
9943
|
-
true
|
|
9944
|
-
>();
|
|
9923
|
+
});
|
|
9945
9924
|
});
|
|
9946
9925
|
|
|
9947
9926
|
it("requires a string parent Output", () => {
|
|
9948
|
-
|
|
9927
|
+
void (() => {
|
|
9949
9928
|
// @ts-expect-error Regex constraints require string Outputs.
|
|
9950
9929
|
regex("NumberPattern", /1/u)(Number);
|
|
9951
|
-
};
|
|
9952
|
-
|
|
9953
|
-
assertType<
|
|
9954
|
-
typeof compileTimeAssertions extends (
|
|
9955
|
-
...args: Array<never>
|
|
9956
|
-
) => unknown
|
|
9957
|
-
? true
|
|
9958
|
-
: false,
|
|
9959
|
-
true
|
|
9960
|
-
>();
|
|
9930
|
+
});
|
|
9961
9931
|
});
|
|
9962
9932
|
|
|
9963
9933
|
describe("Type", () => {
|
|
@@ -10124,15 +10094,13 @@ describe("BrandFactory", () => {
|
|
|
10124
10094
|
const _unionBrand = "Todo" as "Todo" | "User";
|
|
10125
10095
|
const _broadBrand = "Todo" as string;
|
|
10126
10096
|
const _patternedBrand = "Todo" as `Todo${string}`;
|
|
10127
|
-
|
|
10128
|
-
brand: B,
|
|
10129
|
-
): B => {
|
|
10097
|
+
void (<B extends "Todo" | "User">(brand: B): B => {
|
|
10130
10098
|
// @ts-expect-error An unresolved generic brand might be a union.
|
|
10131
10099
|
createId<B>(deps);
|
|
10132
10100
|
return brand;
|
|
10133
|
-
};
|
|
10101
|
+
});
|
|
10134
10102
|
|
|
10135
|
-
|
|
10103
|
+
void (() => {
|
|
10136
10104
|
// @ts-expect-error A union would assign multiple brands to one Id.
|
|
10137
10105
|
createId<typeof _unionBrand>(deps);
|
|
10138
10106
|
// @ts-expect-error Every Id creator rejects union brands.
|
|
@@ -10147,24 +10115,7 @@ describe("BrandFactory", () => {
|
|
|
10147
10115
|
const _todoId: Id & Brand<"Todo"> = random;
|
|
10148
10116
|
// @ts-expect-error A Todo Id must not satisfy a User Id API.
|
|
10149
10117
|
const _userId: Id & Brand<"User"> = todoRandom;
|
|
10150
|
-
};
|
|
10151
|
-
|
|
10152
|
-
assertType<
|
|
10153
|
-
typeof genericBrandAssertion extends (
|
|
10154
|
-
...args: Array<never>
|
|
10155
|
-
) => unknown
|
|
10156
|
-
? true
|
|
10157
|
-
: false,
|
|
10158
|
-
true
|
|
10159
|
-
>();
|
|
10160
|
-
assertType<
|
|
10161
|
-
typeof compileTimeAssertions extends (
|
|
10162
|
-
...args: Array<never>
|
|
10163
|
-
) => unknown
|
|
10164
|
-
? true
|
|
10165
|
-
: false,
|
|
10166
|
-
true
|
|
10167
|
-
>();
|
|
10118
|
+
});
|
|
10168
10119
|
});
|
|
10169
10120
|
|
|
10170
10121
|
it("validates the encoded representation", () => {
|
|
@@ -10210,57 +10161,29 @@ describe("BrandFactory", () => {
|
|
|
10210
10161
|
assertType<typeof TodoId.Error, TableIdError<"Todo">>();
|
|
10211
10162
|
assertType<typeof value, Id & Brand<"Todo">>();
|
|
10212
10163
|
|
|
10213
|
-
|
|
10214
|
-
// @ts-expect-error A Todo Id must not satisfy a User Id API.
|
|
10215
|
-
const _userId: Id & Brand<"User"> = value;
|
|
10216
|
-
};
|
|
10217
|
-
|
|
10218
|
-
assertType<
|
|
10219
|
-
typeof compileTimeAssertions extends (
|
|
10220
|
-
...args: Array<never>
|
|
10221
|
-
) => unknown
|
|
10222
|
-
? true
|
|
10223
|
-
: false,
|
|
10224
|
-
true
|
|
10225
|
-
>();
|
|
10164
|
+
void (() => {
|
|
10165
|
+
// @ts-expect-error A Todo Id must not satisfy a User Id API.
|
|
10166
|
+
const _userId: Id & Brand<"User"> = value;
|
|
10167
|
+
});
|
|
10226
10168
|
});
|
|
10227
10169
|
|
|
10228
10170
|
it("requires one concrete table name", () => {
|
|
10229
10171
|
const unionTable = "Todo" as "Todo" | "User";
|
|
10230
10172
|
const broadTable = "Todo" as TypeName;
|
|
10231
10173
|
const patternedTable = "Todo" as `Todo${string}`;
|
|
10232
|
-
|
|
10233
|
-
table: Table,
|
|
10234
|
-
): Table => {
|
|
10174
|
+
void (<Table extends "Todo" | "User">(table: Table): Table => {
|
|
10235
10175
|
// @ts-expect-error An unresolved generic table might be a union.
|
|
10236
10176
|
id(table);
|
|
10237
10177
|
return table;
|
|
10238
|
-
};
|
|
10239
|
-
|
|
10178
|
+
});
|
|
10179
|
+
void (() => {
|
|
10240
10180
|
// @ts-expect-error A union would assign multiple table brands to one Id.
|
|
10241
10181
|
id(unionTable);
|
|
10242
10182
|
// @ts-expect-error A widened TypeName does not identify one table.
|
|
10243
10183
|
id(broadTable);
|
|
10244
10184
|
// @ts-expect-error A template pattern does not identify one table.
|
|
10245
10185
|
id(patternedTable);
|
|
10246
|
-
};
|
|
10247
|
-
|
|
10248
|
-
assertType<
|
|
10249
|
-
typeof genericTableAssertion extends (
|
|
10250
|
-
...args: Array<never>
|
|
10251
|
-
) => unknown
|
|
10252
|
-
? true
|
|
10253
|
-
: false,
|
|
10254
|
-
true
|
|
10255
|
-
>();
|
|
10256
|
-
assertType<
|
|
10257
|
-
typeof compileTimeAssertions extends (
|
|
10258
|
-
...args: Array<never>
|
|
10259
|
-
) => unknown
|
|
10260
|
-
? true
|
|
10261
|
-
: false,
|
|
10262
|
-
true
|
|
10263
|
-
>();
|
|
10186
|
+
});
|
|
10264
10187
|
});
|
|
10265
10188
|
|
|
10266
10189
|
it("converts to and from its 16-byte representation", () => {
|
|
@@ -10621,19 +10544,10 @@ describe("BrandFactory", () => {
|
|
|
10621
10544
|
});
|
|
10622
10545
|
|
|
10623
10546
|
it("requires a number parent Output", () => {
|
|
10624
|
-
|
|
10547
|
+
void (() => {
|
|
10625
10548
|
// @ts-expect-error Numeric constraints require number Outputs.
|
|
10626
10549
|
lessThan(1)(String);
|
|
10627
|
-
};
|
|
10628
|
-
|
|
10629
|
-
assertType<
|
|
10630
|
-
typeof compileTimeAssertions extends (
|
|
10631
|
-
...args: Array<never>
|
|
10632
|
-
) => unknown
|
|
10633
|
-
? true
|
|
10634
|
-
: false,
|
|
10635
|
-
true
|
|
10636
|
-
>();
|
|
10550
|
+
});
|
|
10637
10551
|
});
|
|
10638
10552
|
});
|
|
10639
10553
|
|
|
@@ -11015,19 +10929,10 @@ describe("BrandFactory", () => {
|
|
|
11015
10929
|
});
|
|
11016
10930
|
|
|
11017
10931
|
it("requires a DecimalString parent", () => {
|
|
11018
|
-
|
|
10932
|
+
void (() => {
|
|
11019
10933
|
// @ts-expect-error The parent must output DecimalString.
|
|
11020
10934
|
nonNegativeDecimalString(String);
|
|
11021
|
-
};
|
|
11022
|
-
|
|
11023
|
-
assertType<
|
|
11024
|
-
typeof compileTimeAssertions extends (
|
|
11025
|
-
...args: Array<never>
|
|
11026
|
-
) => unknown
|
|
11027
|
-
? true
|
|
11028
|
-
: false,
|
|
11029
|
-
true
|
|
11030
|
-
>();
|
|
10935
|
+
});
|
|
11031
10936
|
});
|
|
11032
10937
|
|
|
11033
10938
|
describe("Type", () => {
|
|
@@ -11244,7 +11149,7 @@ describe("BrandFactory", () => {
|
|
|
11244
11149
|
const value = globalThis.String("0.1");
|
|
11245
11150
|
const unionValue = "0.1" as "0.1" | "0.2";
|
|
11246
11151
|
const positiveDecimalString = PositiveDecimalString.orThrow("0.1");
|
|
11247
|
-
|
|
11152
|
+
void (() => {
|
|
11248
11153
|
// @ts-expect-error The divisor must be an exact decimal string.
|
|
11249
11154
|
multipleOf(0.1);
|
|
11250
11155
|
// @ts-expect-error Arithmetic expressions are numbers, not exact decimal strings.
|
|
@@ -11263,16 +11168,7 @@ describe("BrandFactory", () => {
|
|
|
11263
11168
|
multipleOf(unionValue);
|
|
11264
11169
|
// @ts-expect-error A validated runtime string still has no literal value.
|
|
11265
11170
|
multipleOf(positiveDecimalString);
|
|
11266
|
-
};
|
|
11267
|
-
|
|
11268
|
-
assertType<
|
|
11269
|
-
typeof compileTimeAssertions extends (
|
|
11270
|
-
...args: Array<never>
|
|
11271
|
-
) => unknown
|
|
11272
|
-
? true
|
|
11273
|
-
: false,
|
|
11274
|
-
true
|
|
11275
|
-
>();
|
|
11171
|
+
});
|
|
11276
11172
|
});
|
|
11277
11173
|
|
|
11278
11174
|
it("creates a Brand Factory requiring an exact multiple", () => {
|
|
@@ -11384,19 +11280,10 @@ describe("BrandFactory", () => {
|
|
|
11384
11280
|
});
|
|
11385
11281
|
|
|
11386
11282
|
it("returns a Brand Factory that accepts the parent separately", () => {
|
|
11387
|
-
|
|
11283
|
+
void (() => {
|
|
11388
11284
|
// @ts-expect-error Parameterized Brand Factories accept their parent separately.
|
|
11389
11285
|
between(1, 2, Number);
|
|
11390
|
-
};
|
|
11391
|
-
|
|
11392
|
-
assertType<
|
|
11393
|
-
typeof compileTimeAssertions extends (
|
|
11394
|
-
...args: Array<never>
|
|
11395
|
-
) => unknown
|
|
11396
|
-
? true
|
|
11397
|
-
: false,
|
|
11398
|
-
true
|
|
11399
|
-
>();
|
|
11286
|
+
});
|
|
11400
11287
|
});
|
|
11401
11288
|
});
|
|
11402
11289
|
});
|
|
@@ -11527,6 +11414,66 @@ describe("array", () => {
|
|
|
11527
11414
|
assertSame(array(array(Number)), array(array(Number)));
|
|
11528
11415
|
});
|
|
11529
11416
|
|
|
11417
|
+
it("reuses Array and Set Types of a frozen element Type", () => {
|
|
11418
|
+
const Frozen = globalThis.Object.freeze(brand("Frozen", String));
|
|
11419
|
+
const Frozens = array(Frozen);
|
|
11420
|
+
|
|
11421
|
+
assertSame(array(Frozen), Frozens);
|
|
11422
|
+
assertSame(Frozens.element, Frozen);
|
|
11423
|
+
assertSame(set(Frozen), set(Frozen));
|
|
11424
|
+
assertOk(Frozens.fromUnknown(["a"]), ["a"]);
|
|
11425
|
+
});
|
|
11426
|
+
|
|
11427
|
+
it("creates Array and Set Types of copied element Types", () => {
|
|
11428
|
+
const Label = brand("Label", String);
|
|
11429
|
+
const Labels = array(Label);
|
|
11430
|
+
const LabelSet = set(Label);
|
|
11431
|
+
const copies = [
|
|
11432
|
+
{ ...Label },
|
|
11433
|
+
globalThis.Object.freeze({ ...Label }),
|
|
11434
|
+
globalThis.Object.create(Label) as typeof Label,
|
|
11435
|
+
];
|
|
11436
|
+
|
|
11437
|
+
for (const copy of copies) {
|
|
11438
|
+
const Copies = array(copy);
|
|
11439
|
+
assertFalse(globalThis.Object.is(Copies, Labels));
|
|
11440
|
+
assertSame(Copies.element, copy);
|
|
11441
|
+
assertSame(array(copy), Copies);
|
|
11442
|
+
assertSame(set(copy).element, copy);
|
|
11443
|
+
assertSame(set(copy), set(copy));
|
|
11444
|
+
}
|
|
11445
|
+
assertSame(array(Label), Labels);
|
|
11446
|
+
assertSame(set(Label), LabelSet);
|
|
11447
|
+
});
|
|
11448
|
+
|
|
11449
|
+
it("keeps cached Collection Types out of localized element Types", () => {
|
|
11450
|
+
const Label = minLength(1)(String);
|
|
11451
|
+
const Labels = array(Label);
|
|
11452
|
+
set(Label);
|
|
11453
|
+
const LocalizedLabel = localizeTypes(
|
|
11454
|
+
{ Label },
|
|
11455
|
+
{
|
|
11456
|
+
test: {
|
|
11457
|
+
MinLength1: () => "Localized MinLength1.",
|
|
11458
|
+
String: () => "Localized String.",
|
|
11459
|
+
},
|
|
11460
|
+
},
|
|
11461
|
+
).test.Label;
|
|
11462
|
+
const LocalizedLabels = array(LocalizedLabel);
|
|
11463
|
+
|
|
11464
|
+
assertFalse(globalThis.Object.is(LocalizedLabels, Labels));
|
|
11465
|
+
assertSame(LocalizedLabels.element, LocalizedLabel);
|
|
11466
|
+
assertSame(array(LocalizedLabel), LocalizedLabels);
|
|
11467
|
+
assertSame(set(LocalizedLabel).element, LocalizedLabel);
|
|
11468
|
+
|
|
11469
|
+
const result = LocalizedLabels.fromUnknown(null);
|
|
11470
|
+
assertErr(result);
|
|
11471
|
+
assertEqual(
|
|
11472
|
+
LocalizedLabels.formatError(result.error),
|
|
11473
|
+
"A value null is not an array.",
|
|
11474
|
+
);
|
|
11475
|
+
});
|
|
11476
|
+
|
|
11530
11477
|
it("allows heterogeneous element issues in one error", () => {
|
|
11531
11478
|
interface AError extends TypeError<"A"> {
|
|
11532
11479
|
readonly value: number;
|
|
@@ -11573,38 +11520,22 @@ describe("array", () => {
|
|
|
11573
11520
|
});
|
|
11574
11521
|
|
|
11575
11522
|
it("rejects an unresolved generic element Type", () => {
|
|
11576
|
-
|
|
11577
|
-
Element extends typeof String | typeof Number,
|
|
11578
|
-
>(
|
|
11523
|
+
void (<Element extends typeof String | typeof Number>(
|
|
11579
11524
|
element: Element,
|
|
11580
11525
|
): Element => {
|
|
11581
11526
|
// @ts-expect-error An unresolved generic element might be a union.
|
|
11582
11527
|
array(element);
|
|
11583
11528
|
return element;
|
|
11584
|
-
};
|
|
11585
|
-
|
|
11586
|
-
assertType<
|
|
11587
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
11588
|
-
? true
|
|
11589
|
-
: false,
|
|
11590
|
-
true
|
|
11591
|
-
>();
|
|
11529
|
+
});
|
|
11592
11530
|
});
|
|
11593
11531
|
|
|
11594
11532
|
it("rejects an element with erased concrete Type information", () => {
|
|
11595
11533
|
const erased: FormattableTypeNode = brand("Erased", String);
|
|
11596
11534
|
|
|
11597
|
-
|
|
11535
|
+
void (() => {
|
|
11598
11536
|
// @ts-expect-error An element must preserve its concrete Type.
|
|
11599
11537
|
array(erased);
|
|
11600
|
-
};
|
|
11601
|
-
|
|
11602
|
-
assertType<
|
|
11603
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
11604
|
-
? true
|
|
11605
|
-
: false,
|
|
11606
|
-
true
|
|
11607
|
-
>();
|
|
11538
|
+
});
|
|
11608
11539
|
});
|
|
11609
11540
|
});
|
|
11610
11541
|
|
|
@@ -11775,6 +11706,35 @@ describe("array", () => {
|
|
|
11775
11706
|
assertEqual(reads, 0);
|
|
11776
11707
|
});
|
|
11777
11708
|
|
|
11709
|
+
it("rejects index-like keys that are not canonical indices below the length", () => {
|
|
11710
|
+
const Numbers = array(Number);
|
|
11711
|
+
const keys = [
|
|
11712
|
+
"",
|
|
11713
|
+
"01",
|
|
11714
|
+
"-0",
|
|
11715
|
+
"1.0",
|
|
11716
|
+
"4294967295",
|
|
11717
|
+
"4294967296",
|
|
11718
|
+
"12345678901",
|
|
11719
|
+
];
|
|
11720
|
+
const value = globalThis.Object.assign(
|
|
11721
|
+
[1],
|
|
11722
|
+
globalThis.Object.fromEntries(keys.map((key) => [key, 1])),
|
|
11723
|
+
);
|
|
11724
|
+
|
|
11725
|
+
assertFalse(Numbers.is(value));
|
|
11726
|
+
assertEqual(
|
|
11727
|
+
Numbers.fromUnknown(value, { errors: "all" }),
|
|
11728
|
+
err({
|
|
11729
|
+
type: "Array",
|
|
11730
|
+
reason: {
|
|
11731
|
+
kind: "Items",
|
|
11732
|
+
issues: keys.map((key) => ({ kind: "ExcessProperty", key })),
|
|
11733
|
+
},
|
|
11734
|
+
}),
|
|
11735
|
+
);
|
|
11736
|
+
});
|
|
11737
|
+
|
|
11778
11738
|
it("accepts ordinary Record elements through typed operations", () => {
|
|
11779
11739
|
const Values = array(record(String, Number));
|
|
11780
11740
|
const input = [{ value: 1 }];
|
|
@@ -12029,18 +11989,12 @@ describe("array", () => {
|
|
|
12029
11989
|
assertType<Parameters<typeof UserIds.is>, [unknown]>();
|
|
12030
11990
|
assertType<Parameters<typeof UserIds.orNull>, [ReadonlyArray<number>]>();
|
|
12031
11991
|
|
|
12032
|
-
|
|
11992
|
+
void (() => {
|
|
12033
11993
|
// @ts-expect-error Error collection is not observable through `is`.
|
|
12034
11994
|
UserIds.is([], { errors: "all" });
|
|
12035
11995
|
// @ts-expect-error Error collection is not observable through `orNull`.
|
|
12036
11996
|
UserIds.orNull([], { errors: "all" });
|
|
12037
|
-
};
|
|
12038
|
-
assertType<
|
|
12039
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
12040
|
-
? true
|
|
12041
|
-
: false,
|
|
12042
|
-
true
|
|
12043
|
-
>();
|
|
11997
|
+
});
|
|
12044
11998
|
});
|
|
12045
11999
|
|
|
12046
12000
|
describe("is", () => {
|
|
@@ -13165,18 +13119,12 @@ describe("set", () => {
|
|
|
13165
13119
|
|
|
13166
13120
|
const uncertain = Math.random() ? String : Number;
|
|
13167
13121
|
const erased: FormattableTypeNode = String;
|
|
13168
|
-
|
|
13122
|
+
void (() => {
|
|
13169
13123
|
// @ts-expect-error An element must use one concrete Type node.
|
|
13170
13124
|
set(uncertain);
|
|
13171
13125
|
// @ts-expect-error An element must preserve its concrete Type.
|
|
13172
13126
|
set(erased);
|
|
13173
|
-
};
|
|
13174
|
-
assertType<
|
|
13175
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
13176
|
-
? true
|
|
13177
|
-
: false,
|
|
13178
|
-
true
|
|
13179
|
-
>();
|
|
13127
|
+
});
|
|
13180
13128
|
});
|
|
13181
13129
|
|
|
13182
13130
|
it("validates the Set boundary and elements", () => {
|
|
@@ -13362,7 +13310,7 @@ describe("map", () => {
|
|
|
13362
13310
|
|
|
13363
13311
|
const uncertain = Math.random() ? String : Number;
|
|
13364
13312
|
const erased: FormattableTypeNode = String;
|
|
13365
|
-
|
|
13313
|
+
void (() => {
|
|
13366
13314
|
// @ts-expect-error Map key must use one concrete Type node. Pass a Union Type node instead of a union of Type nodes.
|
|
13367
13315
|
map(uncertain, Number);
|
|
13368
13316
|
// @ts-expect-error Map value must use one concrete Type node. Pass a Union Type node instead of a union of Type nodes.
|
|
@@ -13371,13 +13319,7 @@ describe("map", () => {
|
|
|
13371
13319
|
map(erased, Number);
|
|
13372
13320
|
// @ts-expect-error A value must preserve its concrete Type.
|
|
13373
13321
|
map(String, erased);
|
|
13374
|
-
};
|
|
13375
|
-
assertType<
|
|
13376
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
13377
|
-
? true
|
|
13378
|
-
: false,
|
|
13379
|
-
true
|
|
13380
|
-
>();
|
|
13322
|
+
});
|
|
13381
13323
|
});
|
|
13382
13324
|
|
|
13383
13325
|
it("validates the Map boundary, structure, keys, and values", () => {
|
|
@@ -13515,6 +13457,10 @@ describe("map", () => {
|
|
|
13515
13457
|
assertTrue(Counts.is(output));
|
|
13516
13458
|
assertFalse(Counts.is(input));
|
|
13517
13459
|
assertSame(Counts.parent, map(String, String));
|
|
13460
|
+
assertType<
|
|
13461
|
+
typeof Counts.parent,
|
|
13462
|
+
RootMapType<typeof String, typeof String>
|
|
13463
|
+
>();
|
|
13518
13464
|
assertOk(Counts.from.parent(input), output);
|
|
13519
13465
|
assertType<
|
|
13520
13466
|
typeof Counts,
|
|
@@ -13538,6 +13484,15 @@ describe("map", () => {
|
|
|
13538
13484
|
assertSame(map(String, TransparentNumber).to(unchanged), unchanged);
|
|
13539
13485
|
});
|
|
13540
13486
|
|
|
13487
|
+
it("keeps the parent chain finite for widened keys and values", () => {
|
|
13488
|
+
const createLazyMap = (element: AnyType) =>
|
|
13489
|
+
lazy(() => map(element, element));
|
|
13490
|
+
const Names = createLazyMap(String);
|
|
13491
|
+
const names = new Map([["Ada", "Lovelace"]]);
|
|
13492
|
+
|
|
13493
|
+
assertOk(Names.fromUnknown(names), names);
|
|
13494
|
+
});
|
|
13495
|
+
|
|
13541
13496
|
it("rejects decoded key collisions and invalid encoding collisions", () => {
|
|
13542
13497
|
const NumberFromString = setupNumberFromString();
|
|
13543
13498
|
const Counts = map(NumberFromString, Number);
|
|
@@ -13757,7 +13712,7 @@ describe("map", () => {
|
|
|
13757
13712
|
assertEqual(formatError(new Map([["Ada", "bad"]])), "Localized Number.");
|
|
13758
13713
|
assertEqual(formatError(null), "Localized Map.");
|
|
13759
13714
|
|
|
13760
|
-
|
|
13715
|
+
void (() => {
|
|
13761
13716
|
localizeTypes(
|
|
13762
13717
|
{ Scores },
|
|
13763
13718
|
{
|
|
@@ -13788,14 +13743,7 @@ describe("map", () => {
|
|
|
13788
13743
|
},
|
|
13789
13744
|
},
|
|
13790
13745
|
);
|
|
13791
|
-
};
|
|
13792
|
-
|
|
13793
|
-
assertType<
|
|
13794
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
13795
|
-
? true
|
|
13796
|
-
: false,
|
|
13797
|
-
true
|
|
13798
|
-
>();
|
|
13746
|
+
});
|
|
13799
13747
|
});
|
|
13800
13748
|
});
|
|
13801
13749
|
|
|
@@ -13833,6 +13781,10 @@ describe("tuple", () => {
|
|
|
13833
13781
|
readonly [typeof String, typeof NumberFromString, typeof Positive]
|
|
13834
13782
|
>
|
|
13835
13783
|
>();
|
|
13784
|
+
assertType<
|
|
13785
|
+
typeof Entry.parent,
|
|
13786
|
+
RootTupleType<readonly [typeof String, typeof String, typeof Number]>
|
|
13787
|
+
>();
|
|
13836
13788
|
assertType<typeof Entry.Input, readonly [string, string, number]>();
|
|
13837
13789
|
assertType<
|
|
13838
13790
|
typeof Entry.Output,
|
|
@@ -13853,6 +13805,13 @@ describe("tuple", () => {
|
|
|
13853
13805
|
>();
|
|
13854
13806
|
});
|
|
13855
13807
|
|
|
13808
|
+
it("keeps the parent chain finite for widened elements", () => {
|
|
13809
|
+
const createLazyTuple = (element: AnyType) => lazy(() => tuple(element));
|
|
13810
|
+
const Names = createLazyTuple(String);
|
|
13811
|
+
|
|
13812
|
+
assertOk(Names.fromUnknown(["Ada"]), ["Ada"]);
|
|
13813
|
+
});
|
|
13814
|
+
|
|
13856
13815
|
it("creates a root Tuple without a parent", () => {
|
|
13857
13816
|
const Pair = tuple(String, Number);
|
|
13858
13817
|
|
|
@@ -13902,16 +13861,14 @@ describe("tuple", () => {
|
|
|
13902
13861
|
const uncertain: FormattableTypeNode = String;
|
|
13903
13862
|
const widened: NonEmptyReadonlyArray<typeof String> = [String];
|
|
13904
13863
|
|
|
13905
|
-
|
|
13906
|
-
Element extends typeof String | typeof Number,
|
|
13907
|
-
>(
|
|
13864
|
+
void (<Element extends typeof String | typeof Number>(
|
|
13908
13865
|
element: Element,
|
|
13909
13866
|
): Element => {
|
|
13910
13867
|
// @ts-expect-error An unresolved element might be a union of Types.
|
|
13911
13868
|
tuple(element);
|
|
13912
13869
|
return element;
|
|
13913
|
-
};
|
|
13914
|
-
|
|
13870
|
+
});
|
|
13871
|
+
void (<
|
|
13915
13872
|
Elements extends
|
|
13916
13873
|
readonly [typeof String] | readonly [typeof String, typeof Number],
|
|
13917
13874
|
>(
|
|
@@ -13920,35 +13877,16 @@ describe("tuple", () => {
|
|
|
13920
13877
|
// @ts-expect-error A union of Tuple schemas is not one concrete schema.
|
|
13921
13878
|
tuple(...elements);
|
|
13922
13879
|
return elements;
|
|
13923
|
-
};
|
|
13880
|
+
});
|
|
13924
13881
|
|
|
13925
|
-
|
|
13882
|
+
void (() => {
|
|
13926
13883
|
// @ts-expect-error A Tuple must contain at least one Type.
|
|
13927
13884
|
tuple();
|
|
13928
13885
|
// @ts-expect-error An element must preserve its concrete Type.
|
|
13929
13886
|
tuple(uncertain);
|
|
13930
13887
|
// @ts-expect-error A widened array is not a finite Tuple schema.
|
|
13931
13888
|
tuple(...widened);
|
|
13932
|
-
};
|
|
13933
|
-
|
|
13934
|
-
assertType<
|
|
13935
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
13936
|
-
? true
|
|
13937
|
-
: false,
|
|
13938
|
-
true
|
|
13939
|
-
>();
|
|
13940
|
-
assertType<
|
|
13941
|
-
typeof validateUnionElement extends (...args: Array<never>) => unknown
|
|
13942
|
-
? true
|
|
13943
|
-
: false,
|
|
13944
|
-
true
|
|
13945
|
-
>();
|
|
13946
|
-
assertType<
|
|
13947
|
-
typeof validateTupleSchema extends (...args: Array<never>) => unknown
|
|
13948
|
-
? true
|
|
13949
|
-
: false,
|
|
13950
|
-
true
|
|
13951
|
-
>();
|
|
13889
|
+
});
|
|
13952
13890
|
});
|
|
13953
13891
|
});
|
|
13954
13892
|
|
|
@@ -14811,6 +14749,10 @@ describe("record", () => {
|
|
|
14811
14749
|
assertSame(transformed.Values.parent.key, String);
|
|
14812
14750
|
assertSame(transformed.Values.parent.value, String);
|
|
14813
14751
|
assertSame(transformed.Values.parent.parent, null);
|
|
14752
|
+
assertType<
|
|
14753
|
+
typeof transformed.Values.parent,
|
|
14754
|
+
RootRecordType<typeof String, typeof String>
|
|
14755
|
+
>();
|
|
14814
14756
|
assertTrue("parent" in transformed.Values.from);
|
|
14815
14757
|
assertType<
|
|
14816
14758
|
"parent" extends keyof typeof transformed.Values.from ? true : false,
|
|
@@ -14871,11 +14813,19 @@ describe("record", () => {
|
|
|
14871
14813
|
>();
|
|
14872
14814
|
});
|
|
14873
14815
|
|
|
14816
|
+
it("keeps the parent chain finite for widened values", () => {
|
|
14817
|
+
const createLazyRecord = (element: AnyType) =>
|
|
14818
|
+
lazy(() => record(String, element));
|
|
14819
|
+
const Names = createLazyRecord(String);
|
|
14820
|
+
|
|
14821
|
+
assertOk(Names.fromUnknown({ ada: "Lovelace" }), { ada: "Lovelace" });
|
|
14822
|
+
});
|
|
14823
|
+
|
|
14874
14824
|
it("rejects every key in an empty key domain", () => {
|
|
14875
14825
|
const Values = record(Never, Number);
|
|
14876
14826
|
const symbol = globalThis.Symbol();
|
|
14877
14827
|
const value = { anything: 1 };
|
|
14878
|
-
|
|
14828
|
+
void (() => {
|
|
14879
14829
|
Values.from({});
|
|
14880
14830
|
Values.to({});
|
|
14881
14831
|
Values.from({ [symbol]: true });
|
|
@@ -14888,16 +14838,10 @@ describe("record", () => {
|
|
|
14888
14838
|
Values.orNull(value);
|
|
14889
14839
|
// @ts-expect-error A Record with Never keys has no properties.
|
|
14890
14840
|
Values.to(value);
|
|
14891
|
-
};
|
|
14841
|
+
});
|
|
14892
14842
|
|
|
14893
14843
|
assertType<typeof Values.Input, Readonly<Record<string, never>>>();
|
|
14894
14844
|
assertType<typeof Values.Output, Readonly<Record<string, never>>>();
|
|
14895
|
-
assertType<
|
|
14896
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
14897
|
-
? true
|
|
14898
|
-
: false,
|
|
14899
|
-
true
|
|
14900
|
-
>();
|
|
14901
14845
|
assertEqual(Values.fromUnknown({}), ok(createNullRecord({})));
|
|
14902
14846
|
assertEqual(
|
|
14903
14847
|
Values.fromUnknown(value),
|
|
@@ -14948,7 +14892,7 @@ describe("record", () => {
|
|
|
14948
14892
|
type Value = typeof String | typeof Number;
|
|
14949
14893
|
const key = String as Key;
|
|
14950
14894
|
const value = String as Value;
|
|
14951
|
-
|
|
14895
|
+
void (() => {
|
|
14952
14896
|
// @ts-expect-error Record key Input and Output must extend string.
|
|
14953
14897
|
record(Number, String);
|
|
14954
14898
|
// @ts-expect-error Record key Input and Output must extend string.
|
|
@@ -14958,14 +14902,7 @@ describe("record", () => {
|
|
|
14958
14902
|
// @ts-expect-error Record value requires one concrete Type node.
|
|
14959
14903
|
record(String, value);
|
|
14960
14904
|
record(String, union(String, Number));
|
|
14961
|
-
};
|
|
14962
|
-
|
|
14963
|
-
assertType<
|
|
14964
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
14965
|
-
? true
|
|
14966
|
-
: false,
|
|
14967
|
-
true
|
|
14968
|
-
>();
|
|
14905
|
+
});
|
|
14969
14906
|
});
|
|
14970
14907
|
|
|
14971
14908
|
it("distributes key and value errors inside one issue container", () => {
|
|
@@ -15400,6 +15337,53 @@ describe("record", () => {
|
|
|
15400
15337
|
);
|
|
15401
15338
|
});
|
|
15402
15339
|
|
|
15340
|
+
it("visits Proxy own keys in their listed order", () => {
|
|
15341
|
+
const Values = record(String, Number);
|
|
15342
|
+
const key = globalThis.Symbol("key");
|
|
15343
|
+
const input = new Proxy(
|
|
15344
|
+
{ value: "wrong", [key]: 1 },
|
|
15345
|
+
{ ownKeys: () => [key, "value"] },
|
|
15346
|
+
);
|
|
15347
|
+
|
|
15348
|
+
assertEqual(
|
|
15349
|
+
Values.fromUnknown(input),
|
|
15350
|
+
err({
|
|
15351
|
+
type: "Record",
|
|
15352
|
+
reason: {
|
|
15353
|
+
kind: "Entries",
|
|
15354
|
+
issues: [
|
|
15355
|
+
{
|
|
15356
|
+
kind: "Key",
|
|
15357
|
+
key,
|
|
15358
|
+
error: { type: "TypeOf", expected: "String", value: key },
|
|
15359
|
+
},
|
|
15360
|
+
],
|
|
15361
|
+
},
|
|
15362
|
+
}),
|
|
15363
|
+
);
|
|
15364
|
+
assertFalse(Values.is(input));
|
|
15365
|
+
});
|
|
15366
|
+
|
|
15367
|
+
it("rejects a symbol key listed first without reading descriptors", () => {
|
|
15368
|
+
const key = globalThis.Symbol("key");
|
|
15369
|
+
let reads = 0;
|
|
15370
|
+
const input = new Proxy(
|
|
15371
|
+
{ value: 1, [key]: 1 },
|
|
15372
|
+
{
|
|
15373
|
+
ownKeys: () => [key, "value"],
|
|
15374
|
+
getOwnPropertyDescriptor: (target, property) => {
|
|
15375
|
+
reads++;
|
|
15376
|
+
return Reflect.getOwnPropertyDescriptor(target, property);
|
|
15377
|
+
},
|
|
15378
|
+
},
|
|
15379
|
+
);
|
|
15380
|
+
|
|
15381
|
+
assertFalse(record(String, Number).is(input));
|
|
15382
|
+
assertEqual(reads, 0);
|
|
15383
|
+
assertFalse(object({}, record(String, Number)).is(input));
|
|
15384
|
+
assertEqual(reads, 0);
|
|
15385
|
+
});
|
|
15386
|
+
|
|
15403
15387
|
it("collects key and value issues in own-key order", () => {
|
|
15404
15388
|
const Values = record(literal("allowed"), Number);
|
|
15405
15389
|
const input = { wrong: "x", allowed: "y" };
|
|
@@ -16622,22 +16606,16 @@ describe("object", () => {
|
|
|
16622
16606
|
it("exposes readonly properties", () => {
|
|
16623
16607
|
const props = { value: String, note: optional(Number) };
|
|
16624
16608
|
const Model = object(props);
|
|
16625
|
-
|
|
16609
|
+
void (() => {
|
|
16626
16610
|
// @ts-expect-error Object properties are readonly.
|
|
16627
16611
|
Model.props.value = String;
|
|
16628
16612
|
// @ts-expect-error Optional Object properties are readonly.
|
|
16629
16613
|
Model.props.note = optional(Number);
|
|
16630
16614
|
// @ts-expect-error The Type wrapped by an optional property is readonly.
|
|
16631
16615
|
Model.props.note.type = Number;
|
|
16632
|
-
};
|
|
16616
|
+
});
|
|
16633
16617
|
|
|
16634
16618
|
assertType<typeof Model.props, Readonly<typeof props>>();
|
|
16635
|
-
assertType<
|
|
16636
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
16637
|
-
? true
|
|
16638
|
-
: false,
|
|
16639
|
-
true
|
|
16640
|
-
>();
|
|
16641
16619
|
});
|
|
16642
16620
|
|
|
16643
16621
|
it("composes declared properties with a Record", () => {
|
|
@@ -16676,16 +16654,14 @@ describe("object", () => {
|
|
|
16676
16654
|
const numbersFromStrings = record(String, NumberFromString);
|
|
16677
16655
|
const recordUnion = Math.random() > 0.5 ? Values : otherRecord;
|
|
16678
16656
|
const erased: TypeNode = Values;
|
|
16679
|
-
|
|
16680
|
-
Rest extends typeof Values | typeof otherRecord,
|
|
16681
|
-
>(
|
|
16657
|
+
void (<Rest extends typeof Values | typeof otherRecord>(
|
|
16682
16658
|
rest: Rest,
|
|
16683
16659
|
): Rest => {
|
|
16684
16660
|
// @ts-expect-error An unresolved generic could be instantiated with a Record Type union.
|
|
16685
16661
|
object({}, rest);
|
|
16686
16662
|
return rest;
|
|
16687
|
-
};
|
|
16688
|
-
|
|
16663
|
+
});
|
|
16664
|
+
void (() => {
|
|
16689
16665
|
object({ count: Number });
|
|
16690
16666
|
object({ count: Number }, Values);
|
|
16691
16667
|
// @ts-expect-error The second argument must be a Record Type.
|
|
@@ -16710,18 +16686,12 @@ describe("object", () => {
|
|
|
16710
16686
|
Model.from({ count: 0, other: "not a number" });
|
|
16711
16687
|
// @ts-expect-error Undeclared properties must match the Record value Output.
|
|
16712
16688
|
Model.to({ count: 0, other: "not a number" });
|
|
16713
|
-
};
|
|
16689
|
+
});
|
|
16714
16690
|
interface Errors {
|
|
16715
16691
|
readonly count: TypeOfError<"Number">;
|
|
16716
16692
|
}
|
|
16717
16693
|
|
|
16718
16694
|
assertSame(Model.record, Values);
|
|
16719
|
-
assertType<
|
|
16720
|
-
typeof genericRecordAssertion extends (...args: Array<never>) => unknown
|
|
16721
|
-
? true
|
|
16722
|
-
: false,
|
|
16723
|
-
true
|
|
16724
|
-
>();
|
|
16725
16695
|
assertSame(Model.parent, null);
|
|
16726
16696
|
assertType<
|
|
16727
16697
|
typeof Model extends ObjectType<
|
|
@@ -16761,26 +16731,20 @@ describe("object", () => {
|
|
|
16761
16731
|
: false,
|
|
16762
16732
|
false
|
|
16763
16733
|
>();
|
|
16764
|
-
assertType<
|
|
16765
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
16766
|
-
? true
|
|
16767
|
-
: false,
|
|
16768
|
-
true
|
|
16769
|
-
>();
|
|
16770
16734
|
});
|
|
16771
16735
|
|
|
16772
16736
|
it("rejects every property in an empty schema", () => {
|
|
16773
16737
|
const Model = object({});
|
|
16774
16738
|
const symbol = globalThis.Symbol();
|
|
16775
16739
|
const value = { anything: 1 };
|
|
16776
|
-
|
|
16740
|
+
void (() => {
|
|
16777
16741
|
Model.from({ [symbol]: true });
|
|
16778
16742
|
Model.to({ [symbol]: true });
|
|
16779
16743
|
// @ts-expect-error Object.from requires an object value.
|
|
16780
16744
|
Model.from(42);
|
|
16781
16745
|
// @ts-expect-error An empty Object schema has no properties.
|
|
16782
16746
|
Model.from(value);
|
|
16783
|
-
};
|
|
16747
|
+
});
|
|
16784
16748
|
|
|
16785
16749
|
assertType<typeof Model.Input, Readonly<Record<string, never>>>();
|
|
16786
16750
|
assertType<typeof Model.Output, Readonly<Record<string, never>>>();
|
|
@@ -16792,12 +16756,6 @@ describe("object", () => {
|
|
|
16792
16756
|
InferErrors<typeof Model>,
|
|
16793
16757
|
ObjectError<Readonly<Record<never, never>>>
|
|
16794
16758
|
>();
|
|
16795
|
-
assertType<
|
|
16796
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
16797
|
-
? true
|
|
16798
|
-
: false,
|
|
16799
|
-
true
|
|
16800
|
-
>();
|
|
16801
16759
|
assertEqual(
|
|
16802
16760
|
Model.fromUnknown(value),
|
|
16803
16761
|
err({
|
|
@@ -16892,9 +16850,7 @@ describe("object", () => {
|
|
|
16892
16850
|
const optionalProperty: OptionalProperty<Property> = optional(String);
|
|
16893
16851
|
const requiredOrOptional = String as
|
|
16894
16852
|
typeof String | OptionalProperty<typeof String>;
|
|
16895
|
-
|
|
16896
|
-
Property extends typeof String | typeof Number,
|
|
16897
|
-
>(
|
|
16853
|
+
void (<Property extends typeof String | typeof Number>(
|
|
16898
16854
|
genericProperty: Property,
|
|
16899
16855
|
): Property => {
|
|
16900
16856
|
// @ts-expect-error An unresolved generic could be instantiated with a property Type union.
|
|
@@ -16902,33 +16858,19 @@ describe("object", () => {
|
|
|
16902
16858
|
// @ts-expect-error typed preserves Object property validation.
|
|
16903
16859
|
typed("Generic", { value: genericProperty });
|
|
16904
16860
|
return genericProperty;
|
|
16905
|
-
};
|
|
16906
|
-
|
|
16861
|
+
});
|
|
16862
|
+
void (() => {
|
|
16907
16863
|
// @ts-expect-error A property requires one concrete Type node.
|
|
16908
16864
|
object({ value: property });
|
|
16909
16865
|
// @ts-expect-error An optional property requires one concrete Type node.
|
|
16910
16866
|
optional(property);
|
|
16911
|
-
// @ts-expect-error An Object property requires one concrete wrapped Type node.
|
|
16912
|
-
object({ value: optionalProperty });
|
|
16913
|
-
// @ts-expect-error Requiredness must not depend on a runtime union.
|
|
16914
|
-
object({ value: requiredOrOptional });
|
|
16915
|
-
};
|
|
16916
|
-
|
|
16917
|
-
assertType<Property extends OptionalParameter ? true : false, false>();
|
|
16918
|
-
assertType<
|
|
16919
|
-
typeof genericCompileTimeAssertion extends (
|
|
16920
|
-
...args: Array<never>
|
|
16921
|
-
) => unknown
|
|
16922
|
-
? true
|
|
16923
|
-
: false,
|
|
16924
|
-
true
|
|
16925
|
-
>();
|
|
16926
|
-
assertType<
|
|
16927
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
16928
|
-
? true
|
|
16929
|
-
: false,
|
|
16930
|
-
true
|
|
16931
|
-
>();
|
|
16867
|
+
// @ts-expect-error An Object property requires one concrete wrapped Type node.
|
|
16868
|
+
object({ value: optionalProperty });
|
|
16869
|
+
// @ts-expect-error Requiredness must not depend on a runtime union.
|
|
16870
|
+
object({ value: requiredOrOptional });
|
|
16871
|
+
});
|
|
16872
|
+
|
|
16873
|
+
assertType<Property extends OptionalParameter ? true : false, false>();
|
|
16932
16874
|
});
|
|
16933
16875
|
|
|
16934
16876
|
it("rejects erased property Types and a union of schemas", () => {
|
|
@@ -16940,7 +16882,7 @@ describe("object", () => {
|
|
|
16940
16882
|
const getSubsumedProps = (): typeof baseProps | typeof extendedProps =>
|
|
16941
16883
|
Math.random() > 0.5 ? baseProps : extendedProps;
|
|
16942
16884
|
const subsumedProps = getSubsumedProps();
|
|
16943
|
-
|
|
16885
|
+
void (() => {
|
|
16944
16886
|
// @ts-expect-error Optional requires a property Type with concrete information.
|
|
16945
16887
|
optional(type);
|
|
16946
16888
|
// @ts-expect-error Object requires a property Type with a concrete parent.
|
|
@@ -16949,14 +16891,7 @@ describe("object", () => {
|
|
|
16949
16891
|
object(props);
|
|
16950
16892
|
// @ts-expect-error A structurally wider branch is still a schema union.
|
|
16951
16893
|
object(subsumedProps);
|
|
16952
|
-
};
|
|
16953
|
-
|
|
16954
|
-
assertType<
|
|
16955
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
16956
|
-
? true
|
|
16957
|
-
: false,
|
|
16958
|
-
true
|
|
16959
|
-
>();
|
|
16894
|
+
});
|
|
16960
16895
|
});
|
|
16961
16896
|
|
|
16962
16897
|
it("rejects reserved structural error tags in property Types", () => {
|
|
@@ -16995,7 +16930,7 @@ describe("object", () => {
|
|
|
16995
16930
|
formatTestTypeError,
|
|
16996
16931
|
);
|
|
16997
16932
|
const ExcessChild = brand("ExcessChild", Excess);
|
|
16998
|
-
|
|
16933
|
+
void (() => {
|
|
16999
16934
|
// @ts-expect-error ObjectMissingProperty is reserved for Object structure.
|
|
17000
16935
|
object({ value: Missing });
|
|
17001
16936
|
// @ts-expect-error Reserved errors are rejected for optional properties too.
|
|
@@ -17008,14 +16943,7 @@ describe("object", () => {
|
|
|
17008
16943
|
object({ value: optional(Access) });
|
|
17009
16944
|
// @ts-expect-error ObjectExcessProperty is reserved through child Types and optional properties.
|
|
17010
16945
|
object({ value: optional(ExcessChild) });
|
|
17011
|
-
};
|
|
17012
|
-
|
|
17013
|
-
assertType<
|
|
17014
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
17015
|
-
? true
|
|
17016
|
-
: false,
|
|
17017
|
-
true
|
|
17018
|
-
>();
|
|
16946
|
+
});
|
|
17019
16947
|
});
|
|
17020
16948
|
|
|
17021
16949
|
it("requires a fixed set of string property names", () => {
|
|
@@ -17030,7 +16958,7 @@ describe("object", () => {
|
|
|
17030
16958
|
const symbol = globalThis.Symbol();
|
|
17031
16959
|
const number = { 0: String };
|
|
17032
16960
|
const proto = { ["__proto__"]: String };
|
|
17033
|
-
|
|
16961
|
+
void (() => {
|
|
17034
16962
|
// @ts-expect-error Broad keys belong to a Record Type.
|
|
17035
16963
|
object(broad);
|
|
17036
16964
|
// @ts-expect-error Template-pattern keys belong to a Record Type.
|
|
@@ -17045,14 +16973,7 @@ describe("object", () => {
|
|
|
17045
16973
|
object(number);
|
|
17046
16974
|
// @ts-expect-error __proto__ is not an Object property name.
|
|
17047
16975
|
object(proto);
|
|
17048
|
-
};
|
|
17049
|
-
|
|
17050
|
-
assertType<
|
|
17051
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
17052
|
-
? true
|
|
17053
|
-
: false,
|
|
17054
|
-
true
|
|
17055
|
-
>();
|
|
16976
|
+
});
|
|
17056
16977
|
});
|
|
17057
16978
|
});
|
|
17058
16979
|
|
|
@@ -18086,6 +18007,48 @@ describe("object", () => {
|
|
|
18086
18007
|
assertTrue(Model.is({ name: "Ada" }));
|
|
18087
18008
|
});
|
|
18088
18009
|
|
|
18010
|
+
it("compares own keys with the declared keys, not only their count", () => {
|
|
18011
|
+
const Model = object({ name: String, role: String });
|
|
18012
|
+
const reordered = { role: "admin", name: "Ada" };
|
|
18013
|
+
const renamed: unknown = { name: "Ada", title: "admin" };
|
|
18014
|
+
|
|
18015
|
+
const reorderedResult = Model.fromUnknown(reordered);
|
|
18016
|
+
assertOk(reorderedResult);
|
|
18017
|
+
assertSame(reorderedResult.value, reordered);
|
|
18018
|
+
assertTrue(Model.is(reordered));
|
|
18019
|
+
|
|
18020
|
+
const result = Model.fromUnknown(renamed, { errors: "all" });
|
|
18021
|
+
assertEqual(
|
|
18022
|
+
result,
|
|
18023
|
+
err({
|
|
18024
|
+
type: "Object",
|
|
18025
|
+
reason: {
|
|
18026
|
+
kind: "Properties",
|
|
18027
|
+
errors: {
|
|
18028
|
+
role: { type: "ObjectMissingProperty" },
|
|
18029
|
+
title: { type: "ObjectExcessProperty" },
|
|
18030
|
+
},
|
|
18031
|
+
},
|
|
18032
|
+
}),
|
|
18033
|
+
);
|
|
18034
|
+
assertErr(result);
|
|
18035
|
+
assertSame(result.error.reason.kind, "Properties");
|
|
18036
|
+
assertEqual(Reflect.ownKeys(result.error.reason.errors), [
|
|
18037
|
+
"role",
|
|
18038
|
+
"title",
|
|
18039
|
+
]);
|
|
18040
|
+
assertFalse(Model.is(renamed));
|
|
18041
|
+
|
|
18042
|
+
// Position alone must not skip an undeclared key before declared ones.
|
|
18043
|
+
const prefixed = { title: 1, name: "Ada", role: "admin" };
|
|
18044
|
+
assertFalse(Model.is(prefixed));
|
|
18045
|
+
assertFalse(
|
|
18046
|
+
object({ name: String, role: String }, record(String, String)).is(
|
|
18047
|
+
prefixed,
|
|
18048
|
+
),
|
|
18049
|
+
);
|
|
18050
|
+
});
|
|
18051
|
+
|
|
18089
18052
|
it("rejects non-enumerable and symbol excess properties", () => {
|
|
18090
18053
|
const Model = object({ name: String });
|
|
18091
18054
|
const symbol = globalThis.Symbol("excess");
|
|
@@ -18108,6 +18071,44 @@ describe("object", () => {
|
|
|
18108
18071
|
assertEqual(Reflect.ownKeys(errors), ["hidden", symbol]);
|
|
18109
18072
|
});
|
|
18110
18073
|
|
|
18074
|
+
it("visits Proxy own keys in their listed order", () => {
|
|
18075
|
+
const Model = object({ name: String });
|
|
18076
|
+
const symbol = globalThis.Symbol("excess");
|
|
18077
|
+
const value = new Proxy(
|
|
18078
|
+
{ name: "Ada", role: "admin", [symbol]: true },
|
|
18079
|
+
{ ownKeys: () => [symbol, "role", "name"] },
|
|
18080
|
+
);
|
|
18081
|
+
|
|
18082
|
+
const result = Model.fromUnknown(value);
|
|
18083
|
+
assertErr(result);
|
|
18084
|
+
assertSame(result.error.reason.kind, "Properties");
|
|
18085
|
+
assertEqual(Reflect.ownKeys(result.error.reason.errors), [symbol]);
|
|
18086
|
+
assertFalse(Model.is(value));
|
|
18087
|
+
});
|
|
18088
|
+
|
|
18089
|
+
it("copies earlier unchanged Record properties into a decoded object", () => {
|
|
18090
|
+
const Model = object(
|
|
18091
|
+
{ count: Number },
|
|
18092
|
+
record(String, union(Number, setupNumberFromString())),
|
|
18093
|
+
);
|
|
18094
|
+
|
|
18095
|
+
const result = Model.fromUnknown({
|
|
18096
|
+
count: 0,
|
|
18097
|
+
first: 1,
|
|
18098
|
+
second: "2",
|
|
18099
|
+
third: 3,
|
|
18100
|
+
});
|
|
18101
|
+
|
|
18102
|
+
assertOk(result, { count: 0, first: 1, second: 2, third: 3 });
|
|
18103
|
+
assertSame(globalThis.Object.getPrototypeOf(result.value), null);
|
|
18104
|
+
assertEqual(globalThis.Object.keys(result.value), [
|
|
18105
|
+
"count",
|
|
18106
|
+
"first",
|
|
18107
|
+
"second",
|
|
18108
|
+
"third",
|
|
18109
|
+
]);
|
|
18110
|
+
});
|
|
18111
|
+
|
|
18111
18112
|
it("rejects exotic declared and Record properties without reading them", () => {
|
|
18112
18113
|
const Model = object({ count: Number }, record(String, Number));
|
|
18113
18114
|
let reads = 0;
|
|
@@ -18562,6 +18563,43 @@ describe("object", () => {
|
|
|
18562
18563
|
});
|
|
18563
18564
|
});
|
|
18564
18565
|
|
|
18566
|
+
it("validates Record property Outputs without decoding them", () => {
|
|
18567
|
+
const Model = object({}, record(String, setupNumberFromString()));
|
|
18568
|
+
const invalid = {
|
|
18569
|
+
first: 1,
|
|
18570
|
+
second: "2",
|
|
18571
|
+
} as unknown as typeof Model.Output;
|
|
18572
|
+
|
|
18573
|
+
assertAssertionError(() => Model.from(invalid), "Expected Object.", {
|
|
18574
|
+
type: "Object",
|
|
18575
|
+
reason: {
|
|
18576
|
+
kind: "Properties",
|
|
18577
|
+
errors: {
|
|
18578
|
+
second: {
|
|
18579
|
+
type: "Record",
|
|
18580
|
+
reason: {
|
|
18581
|
+
kind: "Entries",
|
|
18582
|
+
issues: [
|
|
18583
|
+
{
|
|
18584
|
+
kind: "Value",
|
|
18585
|
+
key: "second",
|
|
18586
|
+
error: {
|
|
18587
|
+
type: "NumberFromString",
|
|
18588
|
+
outputError: {
|
|
18589
|
+
type: "TypeOf",
|
|
18590
|
+
expected: "Number",
|
|
18591
|
+
value: "2",
|
|
18592
|
+
},
|
|
18593
|
+
},
|
|
18594
|
+
},
|
|
18595
|
+
],
|
|
18596
|
+
},
|
|
18597
|
+
},
|
|
18598
|
+
},
|
|
18599
|
+
},
|
|
18600
|
+
});
|
|
18601
|
+
});
|
|
18602
|
+
|
|
18565
18603
|
it("validates every property chain from its parent in declaration order", () => {
|
|
18566
18604
|
const {
|
|
18567
18605
|
Model,
|
|
@@ -18638,10 +18676,10 @@ describe("object", () => {
|
|
|
18638
18676
|
const Model = object({ name: String });
|
|
18639
18677
|
const symbol = globalThis.Symbol();
|
|
18640
18678
|
const value = { name: "Ada", [symbol]: true };
|
|
18641
|
-
|
|
18679
|
+
void (() => {
|
|
18642
18680
|
Model.from(value);
|
|
18643
18681
|
Model.to(value);
|
|
18644
|
-
};
|
|
18682
|
+
});
|
|
18645
18683
|
|
|
18646
18684
|
assertType<
|
|
18647
18685
|
typeof Model.Input extends {
|
|
@@ -18667,12 +18705,6 @@ describe("object", () => {
|
|
|
18667
18705
|
{ readonly name: string } extends typeof Model.Output ? true : false,
|
|
18668
18706
|
true
|
|
18669
18707
|
>();
|
|
18670
|
-
assertType<
|
|
18671
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
18672
|
-
? true
|
|
18673
|
-
: false,
|
|
18674
|
-
true
|
|
18675
|
-
>();
|
|
18676
18708
|
});
|
|
18677
18709
|
|
|
18678
18710
|
it("return valid Object and Record values", () => {
|
|
@@ -18756,7 +18788,7 @@ describe("object", () => {
|
|
|
18756
18788
|
name: String,
|
|
18757
18789
|
};
|
|
18758
18790
|
const uncertain = String as typeof String | typeof Number;
|
|
18759
|
-
|
|
18791
|
+
void (() => {
|
|
18760
18792
|
// @ts-expect-error Partial Object properties require fixed keys.
|
|
18761
18793
|
partial(dynamic);
|
|
18762
18794
|
// @ts-expect-error Nullable Object properties require fixed keys.
|
|
@@ -18765,14 +18797,7 @@ describe("object", () => {
|
|
|
18765
18797
|
partial({ value: uncertain });
|
|
18766
18798
|
// @ts-expect-error Nullable Object properties require one concrete Type node.
|
|
18767
18799
|
nullableToOptional({ value: uncertain });
|
|
18768
|
-
};
|
|
18769
|
-
|
|
18770
|
-
assertType<
|
|
18771
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
18772
|
-
? true
|
|
18773
|
-
: false,
|
|
18774
|
-
true
|
|
18775
|
-
>();
|
|
18800
|
+
});
|
|
18776
18801
|
});
|
|
18777
18802
|
|
|
18778
18803
|
describe("omit", () => {
|
|
@@ -18796,15 +18821,12 @@ describe("object", () => {
|
|
|
18796
18821
|
assertEqual(Empty.props, {});
|
|
18797
18822
|
assertType<typeof Empty, ObjectType<{}>>();
|
|
18798
18823
|
|
|
18799
|
-
|
|
18800
|
-
key: "name" | "age",
|
|
18801
|
-
keys: ReadonlyArray<"name" | "age">,
|
|
18802
|
-
) => {
|
|
18824
|
+
void ((key: "name" | "age", keys: ReadonlyArray<"name" | "age">) => {
|
|
18803
18825
|
// @ts-expect-error A runtime key must identify one statically known property.
|
|
18804
18826
|
omit(User, key);
|
|
18805
18827
|
// @ts-expect-error A runtime array does not guarantee which properties are omitted.
|
|
18806
18828
|
omit(User, ...keys);
|
|
18807
|
-
};
|
|
18829
|
+
});
|
|
18808
18830
|
|
|
18809
18831
|
const Metadata = object(
|
|
18810
18832
|
{ name: String, age: Number },
|
|
@@ -18824,14 +18846,6 @@ describe("object", () => {
|
|
|
18824
18846
|
MetadataWithoutAge.fromUnknown({ name: "Ada", age: "unknown" }),
|
|
18825
18847
|
{ name: "Ada", age: "unknown" },
|
|
18826
18848
|
);
|
|
18827
|
-
assertType<
|
|
18828
|
-
typeof compileTimeAssertions extends (
|
|
18829
|
-
...args: Array<never>
|
|
18830
|
-
) => unknown
|
|
18831
|
-
? true
|
|
18832
|
-
: false,
|
|
18833
|
-
true
|
|
18834
|
-
>();
|
|
18835
18849
|
});
|
|
18836
18850
|
|
|
18837
18851
|
it("retains defaulted fields and can remove every default", () => {
|
|
@@ -18906,10 +18920,10 @@ describe("result", () => {
|
|
|
18906
18920
|
const Pair = tuple(String, Number);
|
|
18907
18921
|
const PairResult = result(Pair, Pair);
|
|
18908
18922
|
const NeverResult = result(Never, Never);
|
|
18909
|
-
|
|
18923
|
+
void (() => {
|
|
18910
18924
|
// @ts-expect-error The exact ok discriminator must select the value branch.
|
|
18911
18925
|
StringResult.from({ ok: false, value: "value" });
|
|
18912
|
-
};
|
|
18926
|
+
});
|
|
18913
18927
|
|
|
18914
18928
|
assertEqual(StringResult.name, "DiscriminatedUnion");
|
|
18915
18929
|
assertEqual(StringResult.key, "ok");
|
|
@@ -18929,12 +18943,6 @@ describe("result", () => {
|
|
|
18929
18943
|
]
|
|
18930
18944
|
>
|
|
18931
18945
|
>();
|
|
18932
|
-
assertType<
|
|
18933
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
18934
|
-
? true
|
|
18935
|
-
: false,
|
|
18936
|
-
true
|
|
18937
|
-
>();
|
|
18938
18946
|
assertOk(StringResult.fromUnknown({ ok: true, value: "value" }), {
|
|
18939
18947
|
ok: true,
|
|
18940
18948
|
value: "value",
|
|
@@ -19037,19 +19045,13 @@ describe("typed", () => {
|
|
|
19037
19045
|
const Message = discriminatedUnion(Create, Delete);
|
|
19038
19046
|
type Message = typeof Message.Output;
|
|
19039
19047
|
type CreateMessage = ExtractTyped<Message, "Create">;
|
|
19040
|
-
|
|
19048
|
+
void (() => {
|
|
19041
19049
|
// @ts-expect-error The selected type must exist in the Output union.
|
|
19042
19050
|
type Typo = ExtractTyped<Message, "Cretae">;
|
|
19043
19051
|
assertType<Typo, never>();
|
|
19044
|
-
};
|
|
19052
|
+
});
|
|
19045
19053
|
|
|
19046
19054
|
assertType<CreateMessage, typeof Create.Output>();
|
|
19047
|
-
assertType<
|
|
19048
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
19049
|
-
? true
|
|
19050
|
-
: false,
|
|
19051
|
-
true
|
|
19052
|
-
>();
|
|
19053
19055
|
});
|
|
19054
19056
|
|
|
19055
19057
|
it("creates a strict Object with only a literal type property", () => {
|
|
@@ -19340,12 +19342,12 @@ describe("typed", () => {
|
|
|
19340
19342
|
ValuesWithTag,
|
|
19341
19343
|
);
|
|
19342
19344
|
const value = { type: "Open", label: "Label", note: "Note" } as const;
|
|
19343
|
-
|
|
19345
|
+
void (() => {
|
|
19344
19346
|
// @ts-expect-error Additional properties must match the Record value Type.
|
|
19345
19347
|
Open.from({ type: "Open", label: "Label", score: 1 });
|
|
19346
19348
|
// @ts-expect-error The declared CanonicalInput must extend the Record value CanonicalInput.
|
|
19347
19349
|
typed("Open", { fixed: AFromString }, ValuesWithTag);
|
|
19348
|
-
};
|
|
19350
|
+
});
|
|
19349
19351
|
|
|
19350
19352
|
assertSame(Open.record, Values);
|
|
19351
19353
|
assertType<
|
|
@@ -19400,12 +19402,6 @@ describe("typed", () => {
|
|
|
19400
19402
|
},
|
|
19401
19403
|
}),
|
|
19402
19404
|
);
|
|
19403
|
-
assertType<
|
|
19404
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
19405
|
-
? true
|
|
19406
|
-
: false,
|
|
19407
|
-
true
|
|
19408
|
-
>();
|
|
19409
19405
|
});
|
|
19410
19406
|
|
|
19411
19407
|
it("requires one concrete Type name and reserves the type property", () => {
|
|
@@ -19423,14 +19419,12 @@ describe("typed", () => {
|
|
|
19423
19419
|
const recordUnion = Math.random() > 0.5 ? Values : UnknownValues;
|
|
19424
19420
|
const erasedRecord: TypeNode = Values;
|
|
19425
19421
|
const restrictedKeys = record(literal("value"), String);
|
|
19426
|
-
|
|
19427
|
-
tag: Tag,
|
|
19428
|
-
): Tag => {
|
|
19422
|
+
void (<Tag extends "One" | "Two">(tag: Tag): Tag => {
|
|
19429
19423
|
// @ts-expect-error An unresolved generic could be instantiated with a tag union.
|
|
19430
19424
|
typed(tag);
|
|
19431
19425
|
return tag;
|
|
19432
|
-
};
|
|
19433
|
-
|
|
19426
|
+
});
|
|
19427
|
+
void (() => {
|
|
19434
19428
|
// @ts-expect-error Type names start with an uppercase letter.
|
|
19435
19429
|
typed("one");
|
|
19436
19430
|
// @ts-expect-error A union does not identify one concrete Type name.
|
|
@@ -19458,20 +19452,7 @@ describe("typed", () => {
|
|
|
19458
19452
|
typed("One", {}, restrictedKeys);
|
|
19459
19453
|
// @ts-expect-error Declared Inputs and Outputs must extend the Record value Type.
|
|
19460
19454
|
typed("One", { value: Number }, Values);
|
|
19461
|
-
};
|
|
19462
|
-
|
|
19463
|
-
assertType<
|
|
19464
|
-
typeof genericTagAssertion extends (...args: Array<never>) => unknown
|
|
19465
|
-
? true
|
|
19466
|
-
: false,
|
|
19467
|
-
true
|
|
19468
|
-
>();
|
|
19469
|
-
assertType<
|
|
19470
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
19471
|
-
? true
|
|
19472
|
-
: false,
|
|
19473
|
-
true
|
|
19474
|
-
>();
|
|
19455
|
+
});
|
|
19475
19456
|
});
|
|
19476
19457
|
});
|
|
19477
19458
|
|
|
@@ -19479,10 +19460,10 @@ describe("typed", () => {
|
|
|
19479
19460
|
it("rejects missing, mismatched, and excess properties", () => {
|
|
19480
19461
|
const Empty = typed("Empty");
|
|
19481
19462
|
const valueWithExcessProperty = { type: "Empty", extra: true } as const;
|
|
19482
|
-
|
|
19463
|
+
void (() => {
|
|
19483
19464
|
// @ts-expect-error Strict typed Input excludes excess properties.
|
|
19484
19465
|
Empty.from({ type: "Empty", extra: true });
|
|
19485
|
-
};
|
|
19466
|
+
});
|
|
19486
19467
|
|
|
19487
19468
|
assertEqual(
|
|
19488
19469
|
Empty.fromUnknown({}),
|
|
@@ -19506,12 +19487,6 @@ describe("typed", () => {
|
|
|
19506
19487
|
}),
|
|
19507
19488
|
);
|
|
19508
19489
|
assertFalse(Empty.is(valueWithExcessProperty));
|
|
19509
|
-
assertType<
|
|
19510
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
19511
|
-
? true
|
|
19512
|
-
: false,
|
|
19513
|
-
true
|
|
19514
|
-
>();
|
|
19515
19490
|
});
|
|
19516
19491
|
});
|
|
19517
19492
|
|
|
@@ -19771,7 +19746,7 @@ describe("discriminatedUnion", () => {
|
|
|
19771
19746
|
const broadKey = "type" as string;
|
|
19772
19747
|
const unionKey = "type" as "type" | "kind";
|
|
19773
19748
|
const patternedKey = "type" as `type${string}`;
|
|
19774
|
-
|
|
19749
|
+
void (() => {
|
|
19775
19750
|
// @ts-expect-error A Discriminated Union requires at least two members.
|
|
19776
19751
|
discriminatedUnion();
|
|
19777
19752
|
// @ts-expect-error A Discriminated Union requires at least two members.
|
|
@@ -19798,14 +19773,7 @@ describe("discriminatedUnion", () => {
|
|
|
19798
19773
|
discriminatedUnion(unionKey, Valid, typed("Other"));
|
|
19799
19774
|
// @ts-expect-error The explicit discriminator key must not be a template pattern.
|
|
19800
19775
|
discriminatedUnion(patternedKey, Valid, typed("Other"));
|
|
19801
|
-
};
|
|
19802
|
-
|
|
19803
|
-
assertType<
|
|
19804
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
19805
|
-
? true
|
|
19806
|
-
: false,
|
|
19807
|
-
true
|
|
19808
|
-
>();
|
|
19776
|
+
});
|
|
19809
19777
|
});
|
|
19810
19778
|
|
|
19811
19779
|
it("requires one concrete finite member tuple", () => {
|
|
@@ -19830,7 +19798,7 @@ describe("discriminatedUnion", () => {
|
|
|
19830
19798
|
] = [OneWithString, TwoWithString];
|
|
19831
19799
|
const uncertainThird = ThreeWithString as
|
|
19832
19800
|
typeof ThreeWithString | typeof _ThreeWithNumber;
|
|
19833
|
-
|
|
19801
|
+
void (() => {
|
|
19834
19802
|
// @ts-expect-error Members must use one concrete finite tuple.
|
|
19835
19803
|
discriminatedUnion(...members);
|
|
19836
19804
|
// @ts-expect-error Explicit-key members must also use one concrete finite tuple.
|
|
@@ -19841,14 +19809,7 @@ describe("discriminatedUnion", () => {
|
|
|
19841
19809
|
discriminatedUnion(...widenedMembers);
|
|
19842
19810
|
// @ts-expect-error Every member slot must use one concrete Object Type.
|
|
19843
19811
|
discriminatedUnion(OneWithString, TwoWithString, uncertainThird);
|
|
19844
|
-
};
|
|
19845
|
-
|
|
19846
|
-
assertType<
|
|
19847
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
19848
|
-
? true
|
|
19849
|
-
: false,
|
|
19850
|
-
true
|
|
19851
|
-
>();
|
|
19812
|
+
});
|
|
19852
19813
|
});
|
|
19853
19814
|
});
|
|
19854
19815
|
|
|
@@ -19987,10 +19948,10 @@ describe("discriminatedUnion", () => {
|
|
|
19987
19948
|
const Deleted = typed("Deleted", { reason: String });
|
|
19988
19949
|
const Event = discriminatedUnion(Created, Deleted);
|
|
19989
19950
|
const uncorrelated = { type: "Created", reason: "reason" } as const;
|
|
19990
|
-
|
|
19951
|
+
void (() => {
|
|
19991
19952
|
// @ts-expect-error The exact discriminator must correlate with its member Input.
|
|
19992
19953
|
Event.from(uncorrelated);
|
|
19993
|
-
};
|
|
19954
|
+
});
|
|
19994
19955
|
|
|
19995
19956
|
assertType<
|
|
19996
19957
|
typeof uncorrelated extends typeof Event.Input ? true : false,
|
|
@@ -20016,12 +19977,6 @@ describe("discriminatedUnion", () => {
|
|
|
20016
19977
|
},
|
|
20017
19978
|
}),
|
|
20018
19979
|
);
|
|
20019
|
-
assertType<
|
|
20020
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
20021
|
-
? true
|
|
20022
|
-
: false,
|
|
20023
|
-
true
|
|
20024
|
-
>();
|
|
20025
19980
|
});
|
|
20026
19981
|
|
|
20027
19982
|
it("returns routed discriminator, access, and member errors", () => {
|
|
@@ -20397,43 +20352,28 @@ describe("lazy", () => {
|
|
|
20397
20352
|
type Definition = Parameters<typeof lazy<Target>>[0];
|
|
20398
20353
|
type DefinitionOutput = ReturnType<Definition>;
|
|
20399
20354
|
const getTarget = (): Target => chooseTarget(true);
|
|
20400
|
-
|
|
20355
|
+
void (() => {
|
|
20401
20356
|
// @ts-expect-error A Lazy Type definition must return one concrete Type node.
|
|
20402
20357
|
lazy(getTarget);
|
|
20403
20358
|
// @ts-expect-error An inferred Lazy Type definition must also return one concrete Type node.
|
|
20404
20359
|
lazy(() => chooseTarget(true));
|
|
20405
|
-
};
|
|
20360
|
+
});
|
|
20406
20361
|
|
|
20407
20362
|
assertType<Target extends DefinitionOutput ? true : false, false>();
|
|
20408
20363
|
assertType<
|
|
20409
20364
|
DefinitionOutput,
|
|
20410
20365
|
"⛔ Type error: Lazy Type definition must return one concrete Type node. Pass a Union Type node instead of a union of Type nodes."
|
|
20411
20366
|
>();
|
|
20412
|
-
assertType<
|
|
20413
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
20414
|
-
? true
|
|
20415
|
-
: false,
|
|
20416
|
-
true
|
|
20417
|
-
>();
|
|
20418
20367
|
});
|
|
20419
20368
|
|
|
20420
20369
|
it("rejects an unresolved generic definition", () => {
|
|
20421
|
-
|
|
20422
|
-
Target extends typeof String | typeof Number,
|
|
20423
|
-
>(
|
|
20370
|
+
void (<Target extends typeof String | typeof Number>(
|
|
20424
20371
|
target: Target,
|
|
20425
20372
|
): Target => {
|
|
20426
20373
|
// @ts-expect-error An unresolved generic target might be a union.
|
|
20427
20374
|
lazy(() => target);
|
|
20428
20375
|
return target;
|
|
20429
|
-
};
|
|
20430
|
-
|
|
20431
|
-
assertType<
|
|
20432
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
20433
|
-
? true
|
|
20434
|
-
: false,
|
|
20435
|
-
true
|
|
20436
|
-
>();
|
|
20376
|
+
});
|
|
20437
20377
|
});
|
|
20438
20378
|
|
|
20439
20379
|
it("accepts one Union Type node as its definition", () => {
|
|
@@ -21310,6 +21250,20 @@ describe("Data", () => {
|
|
|
21310
21250
|
);
|
|
21311
21251
|
assertFalse(Data.fromUnknown(sparse, { errors: "all" }).ok);
|
|
21312
21252
|
|
|
21253
|
+
// The index after a hole is listed at an earlier position.
|
|
21254
|
+
const trailing = createMutableArray<Data>(2);
|
|
21255
|
+
trailing[1] = 1;
|
|
21256
|
+
assertEqual(
|
|
21257
|
+
Data.fromUnknown(trailing, { errors: "all" }),
|
|
21258
|
+
err({
|
|
21259
|
+
type: "Data",
|
|
21260
|
+
reason: {
|
|
21261
|
+
kind: "Issues",
|
|
21262
|
+
issues: [{ kind: "Hole", path: [0] }],
|
|
21263
|
+
},
|
|
21264
|
+
}),
|
|
21265
|
+
);
|
|
21266
|
+
|
|
21313
21267
|
let reads = 0;
|
|
21314
21268
|
const accessor = createMutableArray<Data>(1);
|
|
21315
21269
|
globalThis.Object.defineProperty(accessor, 0, {
|
|
@@ -22136,7 +22090,7 @@ describe("json", () => {
|
|
|
22136
22090
|
>();
|
|
22137
22091
|
assertType<ReturnType<typeof Person.to>, typeof Person.CanonicalInput>();
|
|
22138
22092
|
|
|
22139
|
-
|
|
22093
|
+
void (() => {
|
|
22140
22094
|
type InvalidCanonicalInput = Type<
|
|
22141
22095
|
"InvalidCanonicalInput",
|
|
22142
22096
|
string,
|
|
@@ -22154,14 +22108,7 @@ describe("json", () => {
|
|
|
22154
22108
|
// The encoder's actual image is finite, but CanonicalInput is number.
|
|
22155
22109
|
// @ts-expect-error Type CanonicalInput must be JSON-compatible.
|
|
22156
22110
|
json(_FiniteAfterNumberTransform, "FiniteAfterNumberTransformJson");
|
|
22157
|
-
};
|
|
22158
|
-
|
|
22159
|
-
assertType<
|
|
22160
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
22161
|
-
? true
|
|
22162
|
-
: false,
|
|
22163
|
-
true
|
|
22164
|
-
>();
|
|
22111
|
+
});
|
|
22165
22112
|
});
|
|
22166
22113
|
|
|
22167
22114
|
it("creates a branded Json Type and total typed conversions", () => {
|
|
@@ -22405,7 +22352,7 @@ describe("json", () => {
|
|
|
22405
22352
|
});
|
|
22406
22353
|
|
|
22407
22354
|
it("accepts only Types with a JSON-compatible canonical Input", () => {
|
|
22408
|
-
|
|
22355
|
+
void (() => {
|
|
22409
22356
|
interface StringTree {
|
|
22410
22357
|
readonly value: string;
|
|
22411
22358
|
readonly children: ReadonlyArray<StringTree>;
|
|
@@ -22762,14 +22709,7 @@ describe("json", () => {
|
|
|
22762
22709
|
|
|
22763
22710
|
// @ts-expect-error Type CanonicalInput must be JSON-compatible.
|
|
22764
22711
|
json(NonCanonicalArrayIndex, "NonCanonicalArrayIndexJson");
|
|
22765
|
-
};
|
|
22766
|
-
|
|
22767
|
-
assertType<
|
|
22768
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
22769
|
-
? true
|
|
22770
|
-
: false,
|
|
22771
|
-
true
|
|
22772
|
-
>();
|
|
22712
|
+
});
|
|
22773
22713
|
});
|
|
22774
22714
|
|
|
22775
22715
|
it("forwards ValidationOptions to the represented Type", () => {
|
|
@@ -22911,7 +22851,7 @@ describe("json", () => {
|
|
|
22911
22851
|
describe("design decisions", () => {
|
|
22912
22852
|
describe("typed inputs", () => {
|
|
22913
22853
|
it("protect append-only fields from incompatible component changes", () => {
|
|
22914
|
-
|
|
22854
|
+
void (() => {
|
|
22915
22855
|
const Todo = object({ title: NonEmptyTrimmedString100 });
|
|
22916
22856
|
|
|
22917
22857
|
// The input guarantees the same constraints as the domain model.
|
|
@@ -22939,18 +22879,11 @@ describe("design decisions", () => {
|
|
|
22939
22879
|
};
|
|
22940
22880
|
|
|
22941
22881
|
return { inputOnChange, longerInputOnChange };
|
|
22942
|
-
};
|
|
22943
|
-
|
|
22944
|
-
assertType<
|
|
22945
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
22946
|
-
? true
|
|
22947
|
-
: false,
|
|
22948
|
-
true
|
|
22949
|
-
>();
|
|
22882
|
+
});
|
|
22950
22883
|
});
|
|
22951
22884
|
|
|
22952
22885
|
it("compose one weaker component output", () => {
|
|
22953
|
-
|
|
22886
|
+
void (() => {
|
|
22954
22887
|
const Todo = object({
|
|
22955
22888
|
title: NonEmptyTrimmedString100,
|
|
22956
22889
|
note: NonEmptyTrimmedString100,
|
|
@@ -22980,18 +22913,11 @@ describe("design decisions", () => {
|
|
|
22980
22913
|
};
|
|
22981
22914
|
|
|
22982
22915
|
return saveTodo;
|
|
22983
|
-
};
|
|
22984
|
-
|
|
22985
|
-
assertType<
|
|
22986
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
22987
|
-
? true
|
|
22988
|
-
: false,
|
|
22989
|
-
true
|
|
22990
|
-
>();
|
|
22916
|
+
});
|
|
22991
22917
|
});
|
|
22992
22918
|
|
|
22993
22919
|
it("compose trim-only component outputs", () => {
|
|
22994
|
-
|
|
22920
|
+
void (() => {
|
|
22995
22921
|
const Todo = object({
|
|
22996
22922
|
title: NonEmptyTrimmedString100,
|
|
22997
22923
|
note: NonEmptyTrimmedString100,
|
|
@@ -23013,14 +22939,7 @@ describe("design decisions", () => {
|
|
|
23013
22939
|
>();
|
|
23014
22940
|
|
|
23015
22941
|
return saveTodo;
|
|
23016
|
-
};
|
|
23017
|
-
|
|
23018
|
-
assertType<
|
|
23019
|
-
typeof compileTimeAssertions extends (...args: Array<never>) => unknown
|
|
23020
|
-
? true
|
|
23021
|
-
: false,
|
|
23022
|
-
true
|
|
23023
|
-
>();
|
|
22942
|
+
});
|
|
23024
22943
|
});
|
|
23025
22944
|
});
|
|
23026
22945
|
|
|
@@ -23380,7 +23299,7 @@ describe("objectKeys", () => {
|
|
|
23380
23299
|
});
|
|
23381
23300
|
|
|
23382
23301
|
it("requires string key codecs and a concrete strict object", () => {
|
|
23383
|
-
|
|
23302
|
+
void (() => {
|
|
23384
23303
|
// @ts-expect-error Key Output must be string.
|
|
23385
23304
|
objectKeys(PortFromString);
|
|
23386
23305
|
// @ts-expect-error objectKeys requires a strict Object Type without a record rest.
|
|
@@ -23392,8 +23311,7 @@ describe("objectKeys", () => {
|
|
|
23392
23311
|
: object({ age: Number });
|
|
23393
23312
|
// @ts-expect-error Output Type must be one concrete Type node. Pass a Union Type node instead of a union of Type nodes.
|
|
23394
23313
|
objectKeys(String)(objectType);
|
|
23395
|
-
};
|
|
23396
|
-
assertType<typeof reject, () => void>();
|
|
23314
|
+
});
|
|
23397
23315
|
});
|
|
23398
23316
|
});
|
|
23399
23317
|
|