ts-fortress 2.0.3 → 2.1.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 CHANGED
@@ -541,6 +541,35 @@ 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
+ // Permissive validation (allow excess properties) - this is the default
557
+ const PermissiveUserType = t.record(
558
+ {
559
+ id: t.string(''),
560
+ name: t.string(''),
561
+ },
562
+ {
563
+ allowExcessProperties: true, // Allow additional properties (default behavior)
564
+ },
565
+ );
566
+
567
+ // Example usage
568
+ const strictData = { id: '123', name: 'John', extra: 'not allowed' };
569
+ console.log(StrictUserType.is(strictData)); // false - 'extra' property causes rejection
570
+
571
+ const permissiveData = { id: '123', name: 'John', extra: 'allowed' };
572
+ console.log(PermissiveUserType.is(permissiveData)); // true - 'extra' property is allowed
544
573
  ```
545
574
 
546
575
  ### Branded Types
@@ -649,6 +678,31 @@ try {
649
678
  } catch (error) {
650
679
  console.error('Validation failed:', error.message);
651
680
  }
681
+
682
+ // Excess property validation example
683
+ const StrictType = t.record(
684
+ {
685
+ name: t.string(''),
686
+ age: t.number(0),
687
+ },
688
+ {
689
+ allowExcessProperties: false,
690
+ },
691
+ );
692
+
693
+ const dataWithExcess = { name: 'John', age: 30, extra: 'not allowed' };
694
+ const strictResult = StrictType.validate(dataWithExcess);
695
+
696
+ if (Result.isErr(strictResult)) {
697
+ console.log(strictResult.value);
698
+ // [{
699
+ // path: ['extra'],
700
+ // actualValue: 'not allowed',
701
+ // expectedType: '{ name: string, age: number }',
702
+ // message: 'Excess property "extra" is not allowed',
703
+ // typeName: '{ name: string, age: number }'
704
+ // }]
705
+ }
652
706
  ```
653
707
 
654
708
  ### ValidationError Structure
@@ -684,7 +738,8 @@ type ValidationError = Readonly<{
684
738
 
685
739
  ### Objects
686
740
 
687
- - `t.record(schema)` - Object validation
741
+ - `t.record(schema, options?)` - Object validation
742
+ - `options.allowExcessProperties?: boolean` - Allow properties not defined in schema (default: true)
688
743
  - `t.keyValueRecord(keyType, valueType)` - Corresponding to the `Record<K, V>` type
689
744
  - `t.partial(recordType)` - Make all fields optional
690
745
  - `t.optional(type)` - Optional field wrapper
@@ -1 +1 @@
1
- {"version":3,"file":"partial.d.mts","sourceRoot":"","sources":["../../src/record/partial.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrD;;;GAGG;AACH,eAAO,MAAM,OAAO,GAClB,KAAK,CAAC,CAAC,SAAS,IAAI,CAAC,aAAa,CAAC,EACnC,KAAK,CAAC,gBAAgB,SAAS,aAAa,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,EAEtE,YAAY,CAAC,EACb,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CACH,KACA,WAAW,CAAC,CAAC,EAAE,gBAAgB,CAsEjC,CAAC;AAEF,KAAK,gBAAgB,CACnB,CAAC,SAAS,IAAI,CAAC,aAAa,CAAC,EAC7B,gBAAgB,SAAS,aAAa,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,SAAS,IAE5E,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,SAAS,IAAI,GAC5C,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAClB,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAElE,KAAK,WAAW,CACd,CAAC,SAAS,IAAI,CAAC,aAAa,CAAC,EAC7B,gBAAgB,SAAS,aAAa,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,SAAS,IAC1E,IAAI,CAAC,gBAAgB,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"partial.d.mts","sourceRoot":"","sources":["../../src/record/partial.mts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,aAAa,CAAC;AAIrD;;;GAGG;AACH,eAAO,MAAM,OAAO,GAClB,KAAK,CAAC,CAAC,SAAS,IAAI,CAAC,aAAa,CAAC,EACnC,KAAK,CAAC,gBAAgB,SAAS,aAAa,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,EAEtE,YAAY,CAAC,EACb,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CACH,KACA,WAAW,CAAC,CAAC,EAAE,gBAAgB,CA0EjC,CAAC;AAEF,KAAK,gBAAgB,CACnB,CAAC,SAAS,IAAI,CAAC,aAAa,CAAC,EAC7B,gBAAgB,SAAS,aAAa,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,SAAS,IAE5E,MAAM,CAAC,gBAAgB,EAAE,SAAS,CAAC,SAAS,IAAI,GAC5C,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAClB,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAElE,KAAK,WAAW,CACd,CAAC,SAAS,IAAI,CAAC,aAAa,CAAC,EAC7B,gBAAgB,SAAS,aAAa,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,SAAS,IAC1E,IAAI,CAAC,gBAAgB,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC"}
@@ -33,8 +33,9 @@ const partial = (recordType, options) => {
33
33
  // overwrite with `a`
34
34
  ...a,
35
35
  };
36
+ const validationResult = recordType.validate(objectFilled);
36
37
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
37
- return recordType.validate(objectFilled);
38
+ return Result.map(validationResult, Obj.pick(Object.keys(a)));
38
39
  };
39
40
  const fill = (a) => {
40
41
  if (!isRecord(a)) {
@@ -1 +1 @@
1
- {"version":3,"file":"partial.mjs","sources":["../../src/record/partial.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AAKA;;;AAGG;MACU,OAAO,GAAG,CAIrB,UAAa,EACb,OAKC,KACmC;AAGpC,IAAA,MAAM,cAAc,GAClB,OAAO,EAAE,QAAQ;AACjB,SAAC,OAAO,EAAE,gBAAgB,KAAK;AAC7B,cAAE,CAAA,QAAA,EAAW,UAAU,CAAC,QAAQ,CAAA,CAAA;AAChC,cAAE,CAAA,iBAAA,EAAoB,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAA,CAAG,CAAC;AAExF,IAAA,MAAM,gBAAgB,GACpB,OAAO,EAAE,gBAAgB,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;;AAGnE,IAAA,MAAM,YAAY,GAAM,GAAG,CAAC,IAAI,CAC9B,UAAU,CAAC,YAAY,EACvB,gBAAgB,CACZ;AAEN,IAAA,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAI;AAC1C,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YAChB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA,8BAA8B,CAAC;AAC7B,oBAAA,WAAW,EAAE,CAAC;AACd,oBAAA,YAAY,EAAE,QAAQ;AACtB,oBAAA,QAAQ,EAAE,cAAc;iBACzB,CAAC;AACH,aAAA,CAAC;;;AAIJ,QAAA,MAAM,YAAY,GAAG;;YAEnB,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,gBAAgB,CAAC;;AAEtD,YAAA,GAAG,CAAC;SACQ;;AAGd,QAAA,OAAO,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAoC;AAC7E,IAAA,CAAC;AAED,IAAA,MAAM,IAAI,GAAoB,CAAC,CAAC,KAAI;AAClC,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;AAChB,YAAA,OAAO,YAAY;;AAGrB,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;QAG7C,OAAO,GAAG,CAAC,IAAI;;AAEb,QAAA,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;;AAElB,QAAA,gBAAgB,CAAC,MAAM,CACrB,CAAC,CAAC;;QAEA,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CACpB,CACc;AACnB,IAAA,CAAC;IAED,OAAO;AACL,QAAA,QAAQ,EAAE,cAAc;QACxB,YAAY;QACZ,IAAI;QACJ,QAAQ;AACR,QAAA,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AACxB,QAAA,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;AAClC,QAAA,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;KAC7B;AACH;AAeA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;"}
1
+ {"version":3,"file":"partial.mjs","sources":["../../src/record/partial.mts"],"sourcesContent":[null],"names":[],"mappings":";;;;;;AAKA;;;AAGG;MACU,OAAO,GAAG,CAIrB,UAAa,EACb,OAKC,KACmC;AAGpC,IAAA,MAAM,cAAc,GAClB,OAAO,EAAE,QAAQ;AACjB,SAAC,OAAO,EAAE,gBAAgB,KAAK;AAC7B,cAAE,CAAA,QAAA,EAAW,UAAU,CAAC,QAAQ,CAAA,CAAA;AAChC,cAAE,CAAA,iBAAA,EAAoB,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAA,CAAG,CAAC;AAExF,IAAA,MAAM,gBAAgB,GACpB,OAAO,EAAE,gBAAgB,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;;AAGnE,IAAA,MAAM,YAAY,GAAM,GAAG,CAAC,IAAI,CAC9B,UAAU,CAAC,YAAY,EACvB,gBAAgB,CACZ;AAEN,IAAA,MAAM,QAAQ,GAAwB,CAAC,CAAC,KAAI;AAC1C,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YAChB,OAAO,MAAM,CAAC,GAAG,CAAC;AAChB,gBAAA,8BAA8B,CAAC;AAC7B,oBAAA,WAAW,EAAE,CAAC;AACd,oBAAA,YAAY,EAAE,QAAQ;AACtB,oBAAA,QAAQ,EAAE,cAAc;iBACzB,CAAC;AACH,aAAA,CAAC;;;AAIJ,QAAA,MAAM,YAAY,GAAG;;YAEnB,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,EAAE,gBAAgB,CAAC;;AAEtD,YAAA,GAAG,CAAC;SACQ;QAEd,MAAM,gBAAgB,GAAG,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC;;AAG1D,QAAA,OAAO,MAAM,CAAC,GAAG,CAAC,gBAAgB,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAE3D;AACH,IAAA,CAAC;AAED,IAAA,MAAM,IAAI,GAAoB,CAAC,CAAC,KAAI;AAClC,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;AAChB,YAAA,OAAO,YAAY;;AAGrB,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;;QAG7C,OAAO,GAAG,CAAC,IAAI;;AAEb,QAAA,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;;AAElB,QAAA,gBAAgB,CAAC,MAAM,CACrB,CAAC,CAAC;;QAEA,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CACpB,CACc;AACnB,IAAA,CAAC;IAED,OAAO;AACL,QAAA,QAAQ,EAAE,cAAc;QACxB,YAAY;QACZ,IAAI;QACJ,QAAQ;AACR,QAAA,EAAE,EAAE,UAAU,CAAC,QAAQ,CAAC;AACxB,QAAA,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC;AAClC,QAAA,IAAI,EAAE,YAAY,CAAC,QAAQ,CAAC;KAC7B;AACH;AAeA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;"}
@@ -1,6 +1,8 @@
1
1
  import { type Type, type TypeOf } from '../type.mjs';
2
2
  export declare const record: <const R extends Record<string, Type<unknown>>>(source: R, options?: Partial<Readonly<{
3
3
  typeName: string;
4
+ /** @default true */
5
+ allowExcessProperties: boolean;
4
6
  }>>) => Type<RecordTypeValue<R>>;
5
7
  type RecordTypeValue<R extends Record<string, Type<unknown>>> = TsFortressInternal.RecordTypeValueImpl<R>;
6
8
  declare namespace TsFortressInternal {
@@ -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,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,EAClE,QAAQ,CAAC,EACT,UAAU,OAAO,CACf,QAAQ,CAAC;IACP,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC,CACH,KACA,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAgFzB,CAAC;AAEF,KAAK,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAC1D,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAE5C,kBAAU,kBAAkB,CAAC;IAC3B,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IACrE,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD,KAAK,sBAAsB,CACzB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,EACvC,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,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI;SAC9D,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"}
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,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,EAClE,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,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAC1D,kBAAkB,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAE5C,kBAAU,kBAAkB,CAAC;IAC3B,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IACrE,sBAAsB,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD,KAAK,sBAAsB,CACzB,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,EACvC,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,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI;SAC9D,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"}
@@ -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 errors = Object.entries(source).flatMap(([k, valueType]) => {
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 (errors.length > 0) {
47
- return Result.err(errors);
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);
@@ -1 +1 @@
1
- {"version":3,"file":"record.mjs","sources":["../../src/record/record.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"record.mjs","sources":["../../src/record/record.mts"],"sourcesContent":[null],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-fortress",
3
- "version": "2.0.3",
3
+ "version": "2.1.0",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "typescript"
@@ -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 { validationErrorsToMessages } from '../validation-error.mjs';
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
 
@@ -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 { validationErrorsToMessages } from '../validation-error.mjs';
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 { validationErrorsToMessages } from '../validation-error.mjs';
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
 
@@ -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 { validationErrorsToMessages } from '../validation-error.mjs';
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 { validationErrorsToMessages } from '../validation-error.mjs';
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 { validationErrorsToMessages } from '../validation-error.mjs';
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 { validationErrorsToMessages } from '../validation-error.mjs';
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);
@@ -1,6 +1,9 @@
1
1
  import { expectType, Result } from 'ts-data-forge';
2
2
  import { type TypeOf } from '../type.mjs';
3
- import { validationErrorsToMessages } from '../validation-error.mjs';
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 { validationErrorsToMessages } from '../validation-error.mjs';
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);