ts-fortress 2.0.4 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -1
- package/dist/index.mjs +1 -1
- package/dist/record/index.mjs +1 -1
- package/dist/record/key-value-record.d.mts +1 -1
- package/dist/record/key-value-record.d.mts.map +1 -1
- package/dist/record/record.d.mts +31 -5
- package/dist/record/record.d.mts.map +1 -1
- package/dist/record/record.mjs +42 -4
- package/dist/record/record.mjs.map +1 -1
- package/package.json +1 -1
- package/src/array/array-of-length.test.mts +16 -1
- package/src/array/array.test.mts +16 -1
- package/src/array/non-empty-array.test.mts +16 -1
- package/src/array/tuple.test.mts +18 -1
- package/src/compose/intersection.test.mts +43 -1
- package/src/compose/merge-records.test.mts +16 -1
- package/src/compose/union.test.mts +16 -1
- package/src/enum/enum.test.mts +16 -1
- package/src/enum/uint-range.test.mts +14 -1
- package/src/primitives/null.test.mts +170 -0
- package/src/primitives/number.test.mts +284 -0
- package/src/primitives/string.test.mts +341 -0
- package/src/primitives/undefined.test.mts +179 -0
- package/src/record/key-value-record.mts +4 -4
- package/src/record/key-value-record.test.mts +28 -2
- package/src/record/keyof.test.mts +12 -0
- package/src/record/partial.test.mts +18 -0
- package/src/record/record-allow-excess-properties.test.mts +180 -0
- package/src/record/record-strict-composition-simple.test.mts +134 -0
- package/src/record/record-strict-composition.test.mts +403 -0
- package/src/record/record.mts +92 -31
- package/src/record/record.test.mts +71 -1
- package/src/record/strict-record.test.mts +192 -0
package/README.md
CHANGED
|
@@ -541,6 +541,58 @@ const UserProfileType = t.record({
|
|
|
541
541
|
}),
|
|
542
542
|
),
|
|
543
543
|
});
|
|
544
|
+
|
|
545
|
+
// Strict validation (disallow excess properties)
|
|
546
|
+
const StrictUserType = t.record(
|
|
547
|
+
{
|
|
548
|
+
id: t.string(''),
|
|
549
|
+
name: t.string(''),
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
allowExcessProperties: false, // Reject any properties not defined in schema
|
|
553
|
+
},
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
// Alternatively, use the strictRecord alias for cleaner syntax
|
|
557
|
+
const StrictUserTypeAlias = t.strictRecord({
|
|
558
|
+
id: t.string(''),
|
|
559
|
+
name: t.string(''),
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// Permissive validation (allow excess properties) - this is the default
|
|
563
|
+
const PermissiveUserType = t.record(
|
|
564
|
+
{
|
|
565
|
+
id: t.string(''),
|
|
566
|
+
name: t.string(''),
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
allowExcessProperties: true, // Allow additional properties (default behavior)
|
|
570
|
+
},
|
|
571
|
+
);
|
|
572
|
+
|
|
573
|
+
// Example usage - both StrictUserType and StrictUserTypeAlias behave identically
|
|
574
|
+
const strictData = { id: '123', name: 'John', extra: 'not allowed' };
|
|
575
|
+
console.log(StrictUserType.is(strictData)); // false - 'extra' property causes rejection
|
|
576
|
+
console.log(StrictUserTypeAlias.is(strictData)); // false - same as above
|
|
577
|
+
|
|
578
|
+
const permissiveData = { id: '123', name: 'John', extra: 'allowed' };
|
|
579
|
+
console.log(PermissiveUserType.is(permissiveData)); // true - 'extra' property is allowed
|
|
580
|
+
|
|
581
|
+
// strictRecord provides cleaner syntax for strict validation
|
|
582
|
+
const UserSchema = t.strictRecord({
|
|
583
|
+
name: t.string(''),
|
|
584
|
+
email: t.string(''),
|
|
585
|
+
age: t.number(0),
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
// Validation examples
|
|
589
|
+
UserSchema.is({ name: 'John', email: 'john@example.com', age: 30 }); // ✅ true
|
|
590
|
+
UserSchema.is({
|
|
591
|
+
name: 'John',
|
|
592
|
+
email: 'john@example.com',
|
|
593
|
+
age: 30,
|
|
594
|
+
role: 'admin',
|
|
595
|
+
}); // ❌ false - excess property
|
|
544
596
|
```
|
|
545
597
|
|
|
546
598
|
### Branded Types
|
|
@@ -649,6 +701,31 @@ try {
|
|
|
649
701
|
} catch (error) {
|
|
650
702
|
console.error('Validation failed:', error.message);
|
|
651
703
|
}
|
|
704
|
+
|
|
705
|
+
// Excess property validation example
|
|
706
|
+
const StrictType = t.record(
|
|
707
|
+
{
|
|
708
|
+
name: t.string(''),
|
|
709
|
+
age: t.number(0),
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
allowExcessProperties: false,
|
|
713
|
+
},
|
|
714
|
+
);
|
|
715
|
+
|
|
716
|
+
const dataWithExcess = { name: 'John', age: 30, extra: 'not allowed' };
|
|
717
|
+
const strictResult = StrictType.validate(dataWithExcess);
|
|
718
|
+
|
|
719
|
+
if (Result.isErr(strictResult)) {
|
|
720
|
+
console.log(strictResult.value);
|
|
721
|
+
// [{
|
|
722
|
+
// path: ['extra'],
|
|
723
|
+
// actualValue: 'not allowed',
|
|
724
|
+
// expectedType: '{ name: string, age: number }',
|
|
725
|
+
// message: 'Excess property "extra" is not allowed',
|
|
726
|
+
// typeName: '{ name: string, age: number }'
|
|
727
|
+
// }]
|
|
728
|
+
}
|
|
652
729
|
```
|
|
653
730
|
|
|
654
731
|
### ValidationError Structure
|
|
@@ -684,7 +761,9 @@ type ValidationError = Readonly<{
|
|
|
684
761
|
|
|
685
762
|
### Objects
|
|
686
763
|
|
|
687
|
-
- `t.record(schema)` - Object validation
|
|
764
|
+
- `t.record(schema, options?)` - Object validation
|
|
765
|
+
- `options.allowExcessProperties?: boolean` - Allow properties not defined in schema (default: true)
|
|
766
|
+
- `t.strictRecord(schema, options?)` - Object validation with strict mode (alias for `record` with `allowExcessProperties: false`)
|
|
688
767
|
- `t.keyValueRecord(keyType, valueType)` - Corresponding to the `Record<K, V>` type
|
|
689
768
|
- `t.partial(recordType)` - Make all fields optional
|
|
690
769
|
- `t.optional(type)` - Optional field wrapper
|
package/dist/index.mjs
CHANGED
|
@@ -39,7 +39,7 @@ export { omit } from './record/omit.mjs';
|
|
|
39
39
|
export { isOptionalProperty, optional } from './record/optional.mjs';
|
|
40
40
|
export { partial } from './record/partial.mjs';
|
|
41
41
|
export { pick } from './record/pick.mjs';
|
|
42
|
-
export { record } from './record/record.mjs';
|
|
42
|
+
export { record, strictRecord } from './record/record.mjs';
|
|
43
43
|
export { unknown } from './unknown.mjs';
|
|
44
44
|
export { createAssertFn } from './utils/create-assert-fn.mjs';
|
|
45
45
|
export { createCastFn } from './utils/create-cast-fn.mjs';
|
package/dist/record/index.mjs
CHANGED
|
@@ -4,5 +4,5 @@ export { omit } from './omit.mjs';
|
|
|
4
4
|
export { isOptionalProperty, optional } from './optional.mjs';
|
|
5
5
|
export { partial } from './partial.mjs';
|
|
6
6
|
export { pick } from './pick.mjs';
|
|
7
|
-
export { record } from './record.mjs';
|
|
7
|
+
export { record, strictRecord } from './record.mjs';
|
|
8
8
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Type, type TypeOf } from '../type.mjs';
|
|
2
|
-
type RecordResultType<K extends Type<string>, V extends Type<unknown>> =
|
|
2
|
+
type RecordResultType<K extends Type<string>, V extends Type<unknown>> = ReadonlyRecord<TypeOf<K>, TypeOf<V>>;
|
|
3
3
|
export declare const keyValueRecord: <K extends Type<string>, V extends Type<unknown>>(keyType: K, valueType: V, options?: Readonly<{
|
|
4
4
|
typeName?: string;
|
|
5
5
|
}>) => Type<RecordResultType<K, V>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"key-value-record.d.mts","sourceRoot":"","sources":["../../src/record/key-value-record.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AASrD,KAAK,gBAAgB,
|
|
1
|
+
{"version":3,"file":"key-value-record.d.mts","sourceRoot":"","sources":["../../src/record/key-value-record.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AASrD,KAAK,gBAAgB,CACnB,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,EACtB,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IACrB,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzC,eAAO,MAAM,cAAc,GAAI,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,EAC5E,SAAS,CAAC,EACV,WAAW,CAAC,EACZ,UAAU,QAAQ,CAAC;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,KACxC,IAAI,CAAC,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAC,CAoF7B,CAAC"}
|
package/dist/record/record.d.mts
CHANGED
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
import { type Type, type TypeOf } from '../type.mjs';
|
|
2
|
-
export declare const record: <const R extends
|
|
2
|
+
export declare const record: <const R extends ReadonlyRecord<string, Type<unknown>>>(source: R, options?: Partial<Readonly<{
|
|
3
3
|
typeName: string;
|
|
4
|
+
/** @default true */
|
|
5
|
+
allowExcessProperties: boolean;
|
|
4
6
|
}>>) => Type<RecordTypeValue<R>>;
|
|
5
|
-
type RecordTypeValue<R extends
|
|
7
|
+
type RecordTypeValue<R extends ReadonlyRecord<string, Type<unknown>>> = TsFortressInternal.RecordTypeValueImpl<R>;
|
|
6
8
|
declare namespace TsFortressInternal {
|
|
7
|
-
export type RecordTypeValueImpl<R extends
|
|
8
|
-
type RecordTypeValueImplSub<A extends
|
|
9
|
+
export type RecordTypeValueImpl<R extends ReadonlyRecord<string, Type<unknown>>> = RecordTypeValueImplSub<R, OptionalTypeKeys<R>>;
|
|
10
|
+
type RecordTypeValueImplSub<A extends ReadonlyRecord<string, Type<unknown>>, OptionalKeys extends keyof A> = Readonly<{
|
|
9
11
|
[key in OptionalKeys]?: TypeOf<A[key]>;
|
|
10
12
|
} & {
|
|
11
13
|
[key in Exclude<keyof A, OptionalKeys>]: TypeOf<A[key]>;
|
|
12
14
|
}>;
|
|
13
|
-
type OptionalTypeKeys<A extends
|
|
15
|
+
type OptionalTypeKeys<A extends ReadonlyRecord<string, Type<unknown>>> = {
|
|
14
16
|
[K in keyof A]: A[K] extends {
|
|
15
17
|
optional: true;
|
|
16
18
|
} ? K : never;
|
|
17
19
|
}[keyof A];
|
|
18
20
|
export {};
|
|
19
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Creates a strict record type that does not allow excess properties.
|
|
24
|
+
* This is an alias for `record(source, { allowExcessProperties: false })`.
|
|
25
|
+
*
|
|
26
|
+
* @param source - The record schema definition
|
|
27
|
+
* @param options - Optional configuration (allowExcessProperties will be overridden to false)
|
|
28
|
+
* @returns A Type that validates records without allowing excess properties
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { strictRecord, string, number } from 'ts-fortress';
|
|
33
|
+
*
|
|
34
|
+
* const User = strictRecord({
|
|
35
|
+
* name: string(),
|
|
36
|
+
* age: number()
|
|
37
|
+
* });
|
|
38
|
+
*
|
|
39
|
+
* User.is({ name: "John", age: 30 }); // true
|
|
40
|
+
* User.is({ name: "John", age: 30, extra: "not allowed" }); // false
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare const strictRecord: <const R extends ReadonlyRecord<string, Type<unknown>>>(source: R, options?: Partial<Readonly<{
|
|
44
|
+
typeName: string;
|
|
45
|
+
}>>) => Type<RecordTypeValue<R>>;
|
|
20
46
|
export {};
|
|
21
47
|
//# sourceMappingURL=record.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.d.mts","sourceRoot":"","sources":["../../src/record/record.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AASrD,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"record.d.mts","sourceRoot":"","sources":["../../src/record/record.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AASrD,eAAO,MAAM,MAAM,GAAI,KAAK,CAAC,CAAC,SAAS,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,EAC1E,QAAQ,CAAC,EACT,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,QAAQ,EAAE,MAAM,CAAC;IAEjB,oBAAoB;IACpB,qBAAqB,EAAE,OAAO,CAAC;CAChC,CAAC,CACH,KACA,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAwGzB,CAAC;AAEF,KAAK,eAAe,CAAC,CAAC,SAAS,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAClE,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAE5C,kBAAU,kBAAkB,CAAC;IAC3B,MAAM,MAAM,mBAAmB,CAC7B,CAAC,SAAS,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAC7C,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnD,KAAK,sBAAsB,CACzB,CAAC,SAAS,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,EAC/C,YAAY,SAAS,MAAM,CAAC,IAC1B,QAAQ,CACV;SACG,GAAG,IAAI,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;KACvC,GAAG;SACD,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;KACxD,CACF,CAAC;IAEF,KAAK,gBAAgB,CAAC,CAAC,SAAS,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI;SACtE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;YAAE,QAAQ,EAAE,IAAI,CAAA;SAAE,GAAG,CAAC,GAAG,KAAK;KAC5D,CAAC,MAAM,CAAC,CAAC,CAAC;;CACZ;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,GACvB,KAAK,CAAC,CAAC,SAAS,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,EAErD,QAAQ,CAAC,EACT,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CACH,KACA,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CACoC,CAAC"}
|
package/dist/record/record.mjs
CHANGED
|
@@ -5,10 +5,12 @@ import { createIsFn } from '../utils/create-is-fn.mjs';
|
|
|
5
5
|
import { createPrimitiveValidationError, prependPathToValidationErrors } from '../validation-error.mjs';
|
|
6
6
|
|
|
7
7
|
const record = (source, options) => {
|
|
8
|
+
const sourceKeys = new Set(Object.keys(source));
|
|
8
9
|
const typeNameFilled = options?.typeName ??
|
|
9
10
|
`{ ${Object.entries(source)
|
|
10
11
|
.map(([k, v]) => `${k}: ${v.typeName}`)
|
|
11
12
|
.join(', ')} }`;
|
|
13
|
+
const allowExcessProperties = options?.allowExcessProperties ?? true;
|
|
12
14
|
const defaultValue =
|
|
13
15
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
14
16
|
Object.fromEntries(Object.entries(source).map(([key, value]) => tp(key, value.defaultValue)));
|
|
@@ -22,7 +24,7 @@ const record = (source, options) => {
|
|
|
22
24
|
}),
|
|
23
25
|
]);
|
|
24
26
|
}
|
|
25
|
-
const
|
|
27
|
+
const defaultErrors = Object.entries(source).flatMap(([k, valueType]) => {
|
|
26
28
|
if (!Object.hasOwn(a, k)) {
|
|
27
29
|
if (source[k]?.optional === true)
|
|
28
30
|
return [];
|
|
@@ -43,8 +45,22 @@ const record = (source, options) => {
|
|
|
43
45
|
}
|
|
44
46
|
return [];
|
|
45
47
|
});
|
|
46
|
-
if
|
|
47
|
-
|
|
48
|
+
// Check for excess properties if allowExcessProperties is false
|
|
49
|
+
if (!allowExcessProperties) {
|
|
50
|
+
const excessKeys = Object.keys(a).filter((key) => !sourceKeys.has(key));
|
|
51
|
+
if (excessKeys.length > 0) {
|
|
52
|
+
const excessErrors = excessKeys.map((key) => ({
|
|
53
|
+
path: [key],
|
|
54
|
+
actualValue: a[key],
|
|
55
|
+
typeName: typeNameFilled,
|
|
56
|
+
expectedType: typeNameFilled,
|
|
57
|
+
message: `Excess property "${key}" is not allowed`,
|
|
58
|
+
}));
|
|
59
|
+
return Result.err([...defaultErrors, ...excessErrors]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (defaultErrors.length > 0) {
|
|
63
|
+
return Result.err(defaultErrors);
|
|
48
64
|
}
|
|
49
65
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
50
66
|
return Result.ok(a);
|
|
@@ -63,6 +79,28 @@ const record = (source, options) => {
|
|
|
63
79
|
cast: createCastFn(validate),
|
|
64
80
|
};
|
|
65
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Creates a strict record type that does not allow excess properties.
|
|
84
|
+
* This is an alias for `record(source, { allowExcessProperties: false })`.
|
|
85
|
+
*
|
|
86
|
+
* @param source - The record schema definition
|
|
87
|
+
* @param options - Optional configuration (allowExcessProperties will be overridden to false)
|
|
88
|
+
* @returns A Type that validates records without allowing excess properties
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* import { strictRecord, string, number } from 'ts-fortress';
|
|
93
|
+
*
|
|
94
|
+
* const User = strictRecord({
|
|
95
|
+
* name: string(),
|
|
96
|
+
* age: number()
|
|
97
|
+
* });
|
|
98
|
+
*
|
|
99
|
+
* User.is({ name: "John", age: 30 }); // true
|
|
100
|
+
* User.is({ name: "John", age: 30, extra: "not allowed" }); // false
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
const strictRecord = (source, options) => record(source, { ...options, allowExcessProperties: false });
|
|
66
104
|
|
|
67
|
-
export { record };
|
|
105
|
+
export { record, strictRecord };
|
|
68
106
|
//# sourceMappingURL=record.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record.mjs","sources":["../../src/record/record.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;MAUa,MAAM,GAAG,CACpB,MAAS,EACT,
|
|
1
|
+
{"version":3,"file":"record.mjs","sources":["../../src/record/record.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { expectType, Result } from 'ts-data-forge';
|
|
2
2
|
import { number } from '../primitives/index.mjs';
|
|
3
3
|
import { type TypeOf } from '../type.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type ValidationError,
|
|
6
|
+
validationErrorsToMessages,
|
|
7
|
+
} from '../validation-error.mjs';
|
|
5
8
|
import { arrayOfLength } from './array-of-length.mjs';
|
|
6
9
|
|
|
7
10
|
describe('arrayOfLength', () => {
|
|
@@ -74,6 +77,18 @@ describe('arrayOfLength', () => {
|
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
describe('validate', () => {
|
|
80
|
+
test('truthy case', () => {
|
|
81
|
+
const ys: unknown = [5, 6, 7, 8];
|
|
82
|
+
|
|
83
|
+
const result = xs.validate(ys);
|
|
84
|
+
expectType<typeof result, Result<Xs, readonly ValidationError[]>>('=');
|
|
85
|
+
expect(Result.isOk(result)).toBe(true);
|
|
86
|
+
|
|
87
|
+
if (Result.isOk(result)) {
|
|
88
|
+
expect(result.value).toStrictEqual([5, 6, 7, 8]);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
77
92
|
test('falsy case 1', () => {
|
|
78
93
|
const ys: unknown = [];
|
|
79
94
|
|
package/src/array/array.test.mts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { expectType, Result } from 'ts-data-forge';
|
|
2
2
|
import { number } from '../primitives/index.mjs';
|
|
3
3
|
import { type TypeOf } from '../type.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type ValidationError,
|
|
6
|
+
validationErrorsToMessages,
|
|
7
|
+
} from '../validation-error.mjs';
|
|
5
8
|
import { array } from './array.mjs';
|
|
6
9
|
|
|
7
10
|
describe('array', () => {
|
|
@@ -58,6 +61,18 @@ describe('array', () => {
|
|
|
58
61
|
});
|
|
59
62
|
|
|
60
63
|
describe('validate', () => {
|
|
64
|
+
test('truthy case', () => {
|
|
65
|
+
const ys: unknown = [1, 2, 3];
|
|
66
|
+
|
|
67
|
+
const result = xs.validate(ys);
|
|
68
|
+
expectType<typeof result, Result<Xs, readonly ValidationError[]>>('=');
|
|
69
|
+
expect(Result.isOk(result)).toBe(true);
|
|
70
|
+
|
|
71
|
+
if (Result.isOk(result)) {
|
|
72
|
+
expect(result.value).toStrictEqual([1, 2, 3]);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
61
76
|
test('falsy case', () => {
|
|
62
77
|
const ys: unknown = ['1', '', 3];
|
|
63
78
|
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { expectType, Result } from 'ts-data-forge';
|
|
2
2
|
import { number } from '../primitives/index.mjs';
|
|
3
3
|
import { type TypeOf } from '../type.mjs';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type ValidationError,
|
|
6
|
+
validationErrorsToMessages,
|
|
7
|
+
} from '../validation-error.mjs';
|
|
5
8
|
import { nonEmptyArray } from './non-empty-array.mjs';
|
|
6
9
|
|
|
7
10
|
describe('nonEmptyArray', () => {
|
|
@@ -74,6 +77,18 @@ describe('nonEmptyArray', () => {
|
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
describe('validate', () => {
|
|
80
|
+
test('truthy case', () => {
|
|
81
|
+
const ys: unknown = [1, 2, 3];
|
|
82
|
+
|
|
83
|
+
const result = xs.validate(ys);
|
|
84
|
+
expectType<typeof result, Result<Xs, readonly ValidationError[]>>('=');
|
|
85
|
+
expect(Result.isOk(result)).toBe(true);
|
|
86
|
+
|
|
87
|
+
if (Result.isOk(result)) {
|
|
88
|
+
expect(result.value).toStrictEqual([1, 2, 3]);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
77
92
|
test('falsy case 1', () => {
|
|
78
93
|
const ys: unknown = [];
|
|
79
94
|
|
package/src/array/tuple.test.mts
CHANGED
|
@@ -2,7 +2,10 @@ import { expectType, Result } from 'ts-data-forge';
|
|
|
2
2
|
import { number, numberLiteral, stringLiteral } from '../primitives/index.mjs';
|
|
3
3
|
import { record } from '../record/index.mjs';
|
|
4
4
|
import { type TypeOf } from '../type.mjs';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type ValidationError,
|
|
7
|
+
validationErrorsToMessages,
|
|
8
|
+
} from '../validation-error.mjs';
|
|
6
9
|
import { tuple } from './tuple.mjs';
|
|
7
10
|
|
|
8
11
|
describe('tuple', () => {
|
|
@@ -72,6 +75,20 @@ describe('tuple', () => {
|
|
|
72
75
|
});
|
|
73
76
|
|
|
74
77
|
describe('validate', () => {
|
|
78
|
+
test('truthy case', () => {
|
|
79
|
+
const x: unknown = [{ x: 1, y: 2 }, 3, '2'];
|
|
80
|
+
|
|
81
|
+
const result = targetType.validate(x);
|
|
82
|
+
expectType<typeof result, Result<TargetType, readonly ValidationError[]>>(
|
|
83
|
+
'=',
|
|
84
|
+
);
|
|
85
|
+
expect(Result.isOk(result)).toBe(true);
|
|
86
|
+
|
|
87
|
+
if (Result.isOk(result)) {
|
|
88
|
+
expect(result.value).toStrictEqual([{ x: 1, y: 2 }, 3, '2']);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
75
92
|
test('falsy case - not array', () => {
|
|
76
93
|
const x: unknown = 'not an array';
|
|
77
94
|
|
|
@@ -3,7 +3,10 @@ import { uintRange } from '../enum/index.mjs';
|
|
|
3
3
|
import { number } from '../primitives/index.mjs';
|
|
4
4
|
import { record } from '../record/index.mjs';
|
|
5
5
|
import { type TypeOf } from '../type.mjs';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
type ValidationError,
|
|
8
|
+
validationErrorsToMessages,
|
|
9
|
+
} from '../validation-error.mjs';
|
|
7
10
|
import { intersection } from './intersection.mjs';
|
|
8
11
|
|
|
9
12
|
describe('intersection', () => {
|
|
@@ -74,6 +77,19 @@ describe('intersection', () => {
|
|
|
74
77
|
});
|
|
75
78
|
|
|
76
79
|
describe('validate', () => {
|
|
80
|
+
test('truthy case', () => {
|
|
81
|
+
const result = targetType.validate({ x: 0, y: 1, z: 2, w: 3 });
|
|
82
|
+
expectType<
|
|
83
|
+
typeof result,
|
|
84
|
+
Result<TargetType, readonly ValidationError[]>
|
|
85
|
+
>('=');
|
|
86
|
+
expect(Result.isOk(result)).toBe(true);
|
|
87
|
+
|
|
88
|
+
if (Result.isOk(result)) {
|
|
89
|
+
expect(result.value).toStrictEqual({ x: 0, y: 1, z: 2, w: 3 });
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
77
93
|
test('falsy case', () => {
|
|
78
94
|
const result = targetType.validate({ x: 0, y: 1 });
|
|
79
95
|
expect(Result.isErr(result)).toBe(true);
|
|
@@ -165,6 +181,19 @@ describe('intersection', () => {
|
|
|
165
181
|
});
|
|
166
182
|
|
|
167
183
|
describe('validate', () => {
|
|
184
|
+
test('truthy case', () => {
|
|
185
|
+
const result = targetType.validate(0);
|
|
186
|
+
expectType<
|
|
187
|
+
typeof result,
|
|
188
|
+
Result<TargetType, readonly ValidationError[]>
|
|
189
|
+
>('=');
|
|
190
|
+
expect(Result.isOk(result)).toBe(true);
|
|
191
|
+
|
|
192
|
+
if (Result.isOk(result)) {
|
|
193
|
+
expect(result.value).toBe(0);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
168
197
|
test('falsy case', () => {
|
|
169
198
|
const result = targetType.validate('aaa');
|
|
170
199
|
expect(Result.isErr(result)).toBe(true);
|
|
@@ -278,6 +307,19 @@ describe('intersection', () => {
|
|
|
278
307
|
});
|
|
279
308
|
|
|
280
309
|
describe('validate', () => {
|
|
310
|
+
test('truthy case', () => {
|
|
311
|
+
const result = targetType.validate(3);
|
|
312
|
+
expectType<
|
|
313
|
+
typeof result,
|
|
314
|
+
Result<TargetType, readonly ValidationError[]>
|
|
315
|
+
>('=');
|
|
316
|
+
expect(Result.isOk(result)).toBe(true);
|
|
317
|
+
|
|
318
|
+
if (Result.isOk(result)) {
|
|
319
|
+
expect(result.value).toBe(3);
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
|
|
281
323
|
test('falsy case', () => {
|
|
282
324
|
const result = targetType.validate(7);
|
|
283
325
|
expect(Result.isErr(result)).toBe(true);
|
|
@@ -2,7 +2,10 @@ import { expectType, Result } from 'ts-data-forge';
|
|
|
2
2
|
import { number } from '../primitives/index.mjs';
|
|
3
3
|
import { record } from '../record/index.mjs';
|
|
4
4
|
import { type TypeOf } from '../type.mjs';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type ValidationError,
|
|
7
|
+
validationErrorsToMessages,
|
|
8
|
+
} from '../validation-error.mjs';
|
|
6
9
|
import { mergeRecords } from './merge-records.mjs';
|
|
7
10
|
|
|
8
11
|
describe('mergeRecords', () => {
|
|
@@ -67,6 +70,18 @@ describe('mergeRecords', () => {
|
|
|
67
70
|
});
|
|
68
71
|
|
|
69
72
|
describe('validate', () => {
|
|
73
|
+
test('truthy case', () => {
|
|
74
|
+
const result = targetType.validate({ x: 0, y: 1, z: 2, w: 3 });
|
|
75
|
+
expectType<typeof result, Result<TargetType, readonly ValidationError[]>>(
|
|
76
|
+
'=',
|
|
77
|
+
);
|
|
78
|
+
expect(Result.isOk(result)).toBe(true);
|
|
79
|
+
|
|
80
|
+
if (Result.isOk(result)) {
|
|
81
|
+
expect(result.value).toStrictEqual({ x: 0, y: 1, z: 2, w: 3 });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
70
85
|
test('falsy case', () => {
|
|
71
86
|
const result = targetType.validate({ x: 0, y: 1 });
|
|
72
87
|
expect(Result.isErr(result)).toBe(true);
|
|
@@ -2,7 +2,10 @@ import { expectType, Result } from 'ts-data-forge';
|
|
|
2
2
|
import { number, numberLiteral, stringLiteral } from '../primitives/index.mjs';
|
|
3
3
|
import { record } from '../record/index.mjs';
|
|
4
4
|
import { type TypeOf } from '../type.mjs';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type ValidationError,
|
|
7
|
+
validationErrorsToMessages,
|
|
8
|
+
} from '../validation-error.mjs';
|
|
6
9
|
import { union } from './union.mjs';
|
|
7
10
|
|
|
8
11
|
describe('union', () => {
|
|
@@ -70,6 +73,18 @@ describe('union', () => {
|
|
|
70
73
|
});
|
|
71
74
|
|
|
72
75
|
describe('validate', () => {
|
|
76
|
+
test('truthy case', () => {
|
|
77
|
+
const result = targetType.validate(3);
|
|
78
|
+
expectType<typeof result, Result<TargetType, readonly ValidationError[]>>(
|
|
79
|
+
'=',
|
|
80
|
+
);
|
|
81
|
+
expect(Result.isOk(result)).toBe(true);
|
|
82
|
+
|
|
83
|
+
if (Result.isOk(result)) {
|
|
84
|
+
expect(result.value).toBe(3);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
73
88
|
test('falsy case', () => {
|
|
74
89
|
const result = targetType.validate(5);
|
|
75
90
|
expect(Result.isErr(result)).toBe(true);
|
package/src/enum/enum.test.mts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { expectType, Result } from 'ts-data-forge';
|
|
2
2
|
import { type TypeOf } from '../type.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type ValidationError,
|
|
5
|
+
validationErrorsToMessages,
|
|
6
|
+
} from '../validation-error.mjs';
|
|
4
7
|
import { enumType } from './enum.mjs';
|
|
5
8
|
|
|
6
9
|
describe('enumType', () => {
|
|
@@ -41,6 +44,18 @@ describe('enumType', () => {
|
|
|
41
44
|
});
|
|
42
45
|
|
|
43
46
|
describe('validate', () => {
|
|
47
|
+
test('truthy case', () => {
|
|
48
|
+
const result = targetType.validate(3);
|
|
49
|
+
expectType<typeof result, Result<TargetType, readonly ValidationError[]>>(
|
|
50
|
+
'=',
|
|
51
|
+
);
|
|
52
|
+
expect(Result.isOk(result)).toBe(true);
|
|
53
|
+
|
|
54
|
+
if (Result.isOk(result)) {
|
|
55
|
+
expect(result.value).toBe(3);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
44
59
|
test('falsy case', () => {
|
|
45
60
|
const result = targetType.validate(5);
|
|
46
61
|
expect(Result.isErr(result)).toBe(true);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { expectType, Result } from 'ts-data-forge';
|
|
2
2
|
import { type TypeOf } from '../type.mjs';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
type ValidationError,
|
|
5
|
+
validationErrorsToMessages,
|
|
6
|
+
} from '../validation-error.mjs';
|
|
4
7
|
import { uintRange } from './uint-range.mjs';
|
|
5
8
|
|
|
6
9
|
describe('uintRange', () => {
|
|
@@ -44,6 +47,16 @@ describe('uintRange', () => {
|
|
|
44
47
|
});
|
|
45
48
|
|
|
46
49
|
describe('validate', () => {
|
|
50
|
+
test('truthy case', () => {
|
|
51
|
+
const result = month.validate(5);
|
|
52
|
+
expectType<typeof result, Result<Month, readonly ValidationError[]>>('=');
|
|
53
|
+
expect(Result.isOk(result)).toBe(true);
|
|
54
|
+
|
|
55
|
+
if (Result.isOk(result)) {
|
|
56
|
+
expect(result.value).toBe(5);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
47
60
|
test('falsy case', () => {
|
|
48
61
|
const result = month.validate(13);
|
|
49
62
|
expect(Result.isErr(result)).toBe(true);
|