@sinclair/typebox 0.24.16 → 0.24.19

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.
@@ -14,7 +14,7 @@ export declare class TypeCheck<T extends Types.TSchema> {
14
14
  /** Returns true if the value matches the given type. */
15
15
  Check(value: unknown): value is Types.Static<T>;
16
16
  }
17
- /** Compiles TypeBox Types for Runtime Type Checking */
17
+ /** Compiles Types for Runtime Type Checking */
18
18
  export declare namespace TypeCompiler {
19
19
  /** Compiles the given type for runtime type checking. This compiler only accepts known TypeBox types non-inclusive of unsafe types. */
20
20
  function Compile<T extends Types.TSchema>(schema: T, references?: Types.TSchema[]): TypeCheck<T>;
@@ -34,10 +34,6 @@ const Types = require("../typebox");
34
34
  // TypeCheck
35
35
  // -------------------------------------------------------------------
36
36
  class TypeCheck {
37
- schema;
38
- references;
39
- checkFunc;
40
- code;
41
37
  constructor(schema, references, checkFunc, code) {
42
38
  this.schema = schema;
43
39
  this.references = references;
@@ -61,7 +57,7 @@ exports.TypeCheck = TypeCheck;
61
57
  // -------------------------------------------------------------------
62
58
  // TypeCompiler
63
59
  // -------------------------------------------------------------------
64
- /** Compiles TypeBox Types for Runtime Type Checking */
60
+ /** Compiles Types for Runtime Type Checking */
65
61
  var TypeCompiler;
66
62
  (function (TypeCompiler) {
67
63
  // -------------------------------------------------------------------
@@ -6,7 +6,7 @@ export interface TExclude<T extends Types.TUnion, U extends Types.TUnion> extend
6
6
  export interface TExtract<T extends Types.TSchema, U extends Types.TUnion> extends Types.TUnion<any[]> {
7
7
  static: Extract<Types.Static<T, this['params']>, Types.Static<U, this['params']>>;
8
8
  }
9
- /** Provides Conditional Types */
9
+ /** Conditional Types */
10
10
  export declare namespace Conditional {
11
11
  /** (Experimental) Creates a conditional expression type */
12
12
  function Extends<L extends Types.TSchema, R extends Types.TSchema, T extends Types.TSchema, U extends Types.TSchema>(left: L, right: R, ok: T, fail: U): TExtends<L, R, T, U>;
@@ -31,7 +31,7 @@ exports.Conditional = void 0;
31
31
  const Types = require("../typebox");
32
32
  const structural_1 = require("./structural");
33
33
  const guard_1 = require("../guard");
34
- /** Provides Conditional Types */
34
+ /** Conditional Types */
35
35
  var Conditional;
36
36
  (function (Conditional) {
37
37
  /** (Experimental) Creates a conditional expression type */
@@ -183,6 +183,9 @@ var Structural;
183
183
  if (AnyOrUnknownRule(right)) {
184
184
  return StructuralResult.True;
185
185
  }
186
+ else if (guard_1.TypeGuard.TObject(right) && globalThis.Object.keys(right.properties).length === 0) {
187
+ return StructuralResult.True;
188
+ }
186
189
  else if (!guard_1.TypeGuard.TConstructor(right)) {
187
190
  return StructuralResult.False;
188
191
  }
@@ -376,8 +379,8 @@ var Structural;
376
379
  return StructuralResult.True;
377
380
  }
378
381
  else if (guard_1.TypeGuard.TObject(right)) {
379
- if (!RecordNumberOrStringKey(left)) {
380
- return Properties(PropertyMap(left), PropertyMap(right));
382
+ if (RecordPattern(left) === '^.*$' && right[Types.Hint] === 'Record') {
383
+ return StructuralResult.True;
381
384
  }
382
385
  else if (RecordPattern(left) === '^.*$') {
383
386
  return StructuralResult.False;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sinclair/typebox",
3
- "version": "0.24.16",
3
+ "version": "0.24.19",
4
4
  "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
5
5
  "keywords": [
6
6
  "json-schema",
@@ -35,7 +35,6 @@
35
35
  "chai": "^4.3.5",
36
36
  "mocha": "^9.2.0",
37
37
  "prettier": "^2.7.1",
38
- "tsd": "^0.19.1",
39
38
  "typescript": "^4.7.4"
40
39
  }
41
40
  }
package/readme.md CHANGED
@@ -45,9 +45,9 @@ type T = Static<typeof T> // type T = string
45
45
 
46
46
  ## Overview
47
47
 
48
- TypeBox is a type builder library that creates in-memory JSON Schema objects that can be statically inferred as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox allows one to compose unified types that can be statically asserted by the TypeScript compiler as well as runtime asserted using standard JSON Schema validation.
48
+ TypeBox is a type builder library that creates in-memory JSON Schema objects that can be statically inferred as TypeScript types. The schemas produced by this library are designed to match the static type checking rules of the TypeScript compiler. TypeBox enables one to create unified types that can be statically checked by TypeScript and runtime asserted using standard JSON Schema validation.
49
49
 
50
- TypeBox can be used as a simple tool to build up complex schemas or integrated into RPC or REST services to help validate JSON data received over the wire. It can be used in both TypeScript and JavaScript environments.
50
+ TypeBox can be used as a simple tool to build up complex schemas or integrated into RPC or REST services to help validate data received over the wire. It offers a type system aligned to the capabilities of TypeScript and can be used equally well in JavaScript environments.
51
51
 
52
52
  License MIT
53
53
 
@@ -69,6 +69,7 @@ License MIT
69
69
  - [Strict](#Strict)
70
70
  - [Validation](#Validation)
71
71
  - [Compiler](#Compiler)
72
+ - [Benchmark](#Benchmark)
72
73
  - [Contribute](#Contribute)
73
74
 
74
75
  <a name="Example"></a>
@@ -140,7 +141,8 @@ type T = Static<typeof T> // type T = {
140
141
  function receive(value: T) { // ... as a Type
141
142
 
142
143
  if(JSON.validate(T, value)) { // ... as a Schema
143
- // ok...
144
+
145
+ // ok...
144
146
  }
145
147
  }
146
148
  ```
@@ -230,7 +232,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
230
232
  │ │ │ }], │
231
233
  │ │ │ additionalItems: false, │
232
234
  │ │ │ minItems: 2, │
233
- │ │ │ maxItems: 2,
235
+ │ │ │ maxItems: 2
234
236
  │ │ │ } │
235
237
  │ │ │ │
236
238
  │ │ │ │
@@ -252,7 +254,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
252
254
  │ y: Type.Number() │ } │ const: 'x' │
253
255
  │ }) │ │ }, { │
254
256
  │ ) │ │ type: 'string', │
255
- │ │ │ const: 'y',
257
+ │ │ │ const: 'y'
256
258
  │ │ │ }] │
257
259
  │ │ │ } │
258
260
  │ │ │ │
@@ -320,7 +322,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
320
322
  │ const T = Type.Pick( │ type T = Pick<{ │ const T = { │
321
323
  │ Type.Object({ │ x: number, │ type: 'object', │
322
324
  │ x: Type.Number(), │ y: number │ properties: { │
323
- │ y: Type.Number(), | }, 'x'> │ x: { │
325
+ │ y: Type.Number() | }, 'x'> │ x: { │
324
326
  │ }), ['x'] │ │ type: 'number' │
325
327
  │ ) │ │ } │
326
328
  │ │ │ }, │
@@ -331,7 +333,7 @@ The following table outlines the TypeBox mappings between TypeScript and JSON sc
331
333
  │ const T = Type.Omit( │ type T = Omit<{ │ const T = { │
332
334
  │ Type.Object({ │ x: number, │ type: 'object', │
333
335
  │ x: Type.Number(), │ y: number │ properties: { │
334
- │ y: Type.Number(), | }, 'x'> │ y: { │
336
+ │ y: Type.Number() | }, 'x'> │ y: { │
335
337
  │ }), ['x'] │ │ type: 'number' │
336
338
  │ ) │ │ } │
337
339
  │ │ │ }, │
@@ -352,8 +354,8 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
352
354
  │ │ │ │
353
355
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
354
356
  │ const T = Type.Object({ │ type T = { │ const T = { │
355
- │ name: Type.Optional( │ name?: string, │ type: 'object', │
356
- │ Type.String(), │ } │ properties: { │
357
+ │ name: Type.Optional( │ name?: string │ type: 'object', │
358
+ │ Type.String() │ } │ properties: { │
357
359
  │ ) │ │ name: { │
358
360
  │ }) │ │ type: 'string' │
359
361
  │ │ │ } │
@@ -362,8 +364,8 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
362
364
  │ │ │ │
363
365
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
364
366
  │ const T = Type.Object({ │ type T = { │ const T = { │
365
- │ name: Type.Readonly( │ readonly name: string, │ type: 'object', │
366
- │ Type.String(), │ } │ properties: { │
367
+ │ name: Type.Readonly( │ readonly name: string │ type: 'object', │
368
+ │ Type.String() │ } │ properties: { │
367
369
  │ ) │ │ name: { │
368
370
  │ }) │ │ type: 'string' │
369
371
  │ │ │ } │
@@ -374,7 +376,7 @@ TypeBox provides modifiers that can be applied to an objects properties. This al
374
376
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
375
377
  │ const T = Type.Object({ │ type T = { │ const T = { │
376
378
  │ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │
377
- │ Type.String(), │ } │ properties: { │
379
+ │ Type.String() │ } │ properties: { │
378
380
  │ ) │ │ name: { │
379
381
  │ }) │ │ type: 'string' │
380
382
  │ │ │ } │
@@ -490,28 +492,28 @@ Use `Type.Recursive(...)` to create recursive types.
490
492
 
491
493
  ```typescript
492
494
  const Node = Type.Recursive(Node => Type.Object({ // const Node = {
493
- id: Type.String(), // $id: "Node",
494
- nodes: Type.Array(Node), // type: "object",
495
+ id: Type.String(), // $id: 'Node',
496
+ nodes: Type.Array(Node) // type: 'object',
495
497
  }), { $id: 'Node' }) // properties: {
496
498
  // id: {
497
- // "type": "string"
499
+ // type: 'string'
498
500
  // },
499
501
  // nodes: {
500
- // type: "array",
502
+ // type: 'array',
501
503
  // items: {
502
- // $ref: "Node"
504
+ // $ref: 'Node'
503
505
  // }
504
506
  // }
505
507
  // },
506
508
  // required: [
507
- // "id",
508
- // "nodes"
509
+ // 'id',
510
+ // 'nodes'
509
511
  // ]
510
512
  // }
511
513
 
512
514
  type Node = Static<typeof Node> // type Node = {
513
515
  // id: string
514
- // nodes: ...[]
516
+ // nodes: Node[]
515
517
  // }
516
518
 
517
519
  function test(node: Node) {
@@ -620,41 +622,41 @@ The following table shows the TypeBox mappings between TypeScript and JSON schem
620
622
 
621
623
  ```typescript
622
624
  ┌────────────────────────────────┬─────────────────────────────┬────────────────────────────────┐
623
- │ TypeBox │ TypeScript │ Extended Schema
625
+ │ TypeBox │ TypeScript │ JSON Schema
624
626
  │ │ │ │
625
627
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
626
628
  │ const T = Conditional.Extends( │ type T = │ const T = { │
627
- │ Type.String(), │ string extends number │ const: false
629
+ │ Type.String(), │ string extends number │ const: false,
628
630
  │ Type.Number(), │ true : false │ type: 'boolean' │
629
- │ Type.Literal(true) │ │ } │
631
+ │ Type.Literal(true), │ │ } │
630
632
  │ Type.Literal(false) │ │ │
631
633
  │ ) │ │ │
632
634
  │ │ │ │
633
635
  ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
634
- │ const T = Conditional.Exclude( │ type T = Exclude< │ const T = { │
635
- │ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │
636
- │ Type.Literal('a') │ 'a' │ const: 'b' │
637
- │ Type.Literal('b') │ > │ type: 'string' │
638
- │ Type.Literal('c') │ │ }, { │
639
- │ ]), │ │ const: 'c' │
640
- │ Type.Union([ │ │ type: 'string' │
641
- │ Type.Literal('a') │ │ }] │
642
- │ ]) │ │ } │
643
- │ ) │ │ │
644
- │ │ │ │
645
- └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
646
636
  │ const T = Conditional.Extract( │ type T = Extract< │ const T = { │
647
637
  │ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │
648
- │ Type.Literal('a') │ 'a' | 'f' │ const: 'a' │
649
- │ Type.Literal('b') │ > │ type: 'string' │
638
+ │ Type.Literal('a'), │ 'a' | 'f' │ const: 'a' │
639
+ │ Type.Literal('b'), │ > │ type: 'string' │
650
640
  │ Type.Literal('c') │ │ }] │
651
641
  │ ]), │ │ } │
652
642
  │ Type.Union([ │ │ │
653
- │ Type.Literal('a') │ │ │
643
+ │ Type.Literal('a'), │ │ │
654
644
  │ Type.Literal('f') │ │ │
655
645
  │ ]) │ │ │
656
646
  │ ) │ │ │
657
647
  │ │ │ │
648
+ ├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤
649
+ │ const T = Conditional.Exclude( │ type T = Exclude< │ const T = { │
650
+ │ Type.Union([ │ 'a' | 'b' | 'c', │ anyOf: [{ │
651
+ │ Type.Literal('a'), │ 'a' │ const: 'b', │
652
+ │ Type.Literal('b'), │ > │ type: 'string' │
653
+ │ Type.Literal('c') │ │ }, { │
654
+ │ ]), │ │ const: 'c', │
655
+ │ Type.Union([ │ │ type: 'string' │
656
+ │ Type.Literal('a') │ │ }] │
657
+ │ ]) │ │ } │
658
+ │ ) │ │ │
659
+ │ │ │ │
658
660
  └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘
659
661
  ```
660
662
 
@@ -670,12 +672,12 @@ import { Type } from '@sinclair/typebox'
670
672
 
671
673
  const T = Type.Object({
672
674
  x: Type.Number({ default: 1 }),
673
- y: Type.Number(),
675
+ y: Type.Number()
674
676
  })
675
677
 
676
678
  const V = Value.Create(T) // const V = {
677
679
  // x: 1,
678
- // y: 0,
680
+ // y: 0
679
681
  // }
680
682
  ```
681
683
  Use `Value.Cast(...)` to cast a value into a given type.
@@ -747,7 +749,7 @@ const U = Type.Strict(T) // const U = {
747
749
 
748
750
  ## Validation
749
751
 
750
- TypeBox schemas target JSON Schema draft 6 so any validator capable of draft 6 should be fine. A good library to use for validation in JavaScript environments is [AJV](https://www.npmjs.com/package/ajv). The following example shows setting up AJV 7 to work with TypeBox.
752
+ TypeBox schemas target JSON Schema draft 6 so any validator capable of draft 6 should be fine. A good library to use for validation in JavaScript environments is [Ajv](https://www.npmjs.com/package/ajv). The following example shows setting up Ajv to work with TypeBox.
751
753
 
752
754
  ```bash
753
755
  $ npm install ajv ajv-formats --save
@@ -756,7 +758,7 @@ $ npm install ajv ajv-formats --save
756
758
  ```typescript
757
759
  //--------------------------------------------------------------------------------------------
758
760
  //
759
- // Import TypeBox and AJV
761
+ // Import TypeBox and Ajv
760
762
  //
761
763
  //--------------------------------------------------------------------------------------------
762
764
 
@@ -766,7 +768,7 @@ import Ajv from 'ajv'
766
768
 
767
769
  //--------------------------------------------------------------------------------------------
768
770
  //
769
- // Setup AJV validator with the following options and formats
771
+ // Setup Ajv validator with the following options and formats
770
772
  //
771
773
  //--------------------------------------------------------------------------------------------
772
774
 
@@ -796,7 +798,7 @@ const ajv = addFormats(new Ajv({}), [
796
798
  const T = Type.Object({
797
799
  x: Type.Number(),
798
800
  y: Type.Number(),
799
- z: Type.Number(),
801
+ z: Type.Number()
800
802
  })
801
803
 
802
804
  //--------------------------------------------------------------------------------------------
@@ -808,13 +810,13 @@ const T = Type.Object({
808
810
  const R = ajv.validate(T, { x: 1, y: 2, z: 3 }) // const R = true
809
811
  ```
810
812
 
811
- Please refer to the official AJV [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using AJV.
813
+ Please refer to the official Ajv [documentation](https://ajv.js.org/guide/getting-started.html) for additional information on using Ajv.
812
814
 
813
815
  <a name="Compiler"></a>
814
816
 
815
817
  ## Compiler
816
818
 
817
- TypeBox provides an optional high performance runtime type checker that can be used in applications that require extremely fast validation. This type checker is optimized for TypeBox types only whose schematics are known in advance. If defining custom schemas with `Type.Unsafe<T>` please consider AJV.
819
+ TypeBox provides an optional high performance runtime type checker that can be used in applications that require extremely fast validation. This type checker is optimized for TypeBox types only whose schematics are known in advance. If defining custom schemas with `Type.Unsafe<T>` please consider Ajv.
818
820
 
819
821
  The following demonstrates its use.
820
822
 
@@ -872,75 +874,78 @@ console.log(C.Code()) // return function check(va
872
874
  // }
873
875
  ```
874
876
 
875
- ### Benchmarks
877
+ ## Benchmark
876
878
 
877
- This project maintains benchmarks that measure TypeBox and AJV compile and validate performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. Results show for AJV version 8.11.0.
879
+ This project maintains benchmarks that measure Ajv and TypeCompiler validation and compilation performance. These benchmarks can be run locally by cloning this repository and running `npm run benchmark`. Results show against Ajv version 8.11.0.
878
880
 
879
- #### Validate
881
+ ### Validate
880
882
 
881
- This benchmark measures overall validate performance. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/check.ts).
883
+ This benchmark measures validation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/check.ts).
882
884
 
883
885
  ```typescript
884
- ┌──────────────────┬────────────┬────────────┬────────────┬────────────────────────┐
885
- │ (index) │ Iterations │ Ajv TypeBox Measured │
886
- ├──────────────────┼────────────┼────────────┼────────────┼────────────────────────┤
887
- │ Number │ 16000000 │ '76ms ' │ '65ms ' │ '1.17 x faster ' │
888
- │ String │ 16000000 │ '272ms ' │ '151ms ' │ '1.80 x faster ' │
889
- │ Boolean │ 16000000 │ '276ms ' │ '149ms ' │ '1.85 x faster ' │
890
- │ Null │ 16000000 │ '272ms ' │ '151ms ' │ '1.80 x faster ' │
891
- │ RegEx │ 16000000 │ '651ms ' │ '555ms ' │ '1.17 x faster ' │
892
- │ ObjectA │ 16000000 │ '496ms ' │ '322ms ' │ '1.54 x faster ' │
893
- │ ObjectB │ 16000000 │ '721ms ' │ '529ms ' │ '1.36 x faster ' │
894
- │ Tuple │ 16000000 │ '332ms ' │ '192ms ' │ '1.73 x faster ' │
895
- │ Union │ 16000000 │ '333ms ' │ '208ms ' │ '1.60 x faster ' │
896
- │ Recursive │ 16000000 │ '5958ms ' │ '2366ms ' │ '2.52 x faster ' │
897
- │ Vector4 │ 16000000 │ '335ms ' │ '171ms ' │ '1.96 x faster ' │
898
- │ Matrix4 │ 16000000 │ '596ms ' │ '410ms ' │ '1.45 x faster ' │
899
- │ Literal_String │ 16000000 │ '273ms ' │ '151ms ' │ '1.81 x faster ' │
900
- │ Literal_Number │ 16000000 │ '261ms ' │ '150ms ' │ '1.74 x faster ' │
901
- │ Literal_Boolean │ 16000000 │ '312ms ' │ '154ms ' │ '2.03 x faster ' │
902
- │ Array_Number │ 16000000 │ '474ms ' │ '237ms ' │ '2.00 x faster ' │
903
- │ Array_String │ 16000000 │ '457ms ' │ '297ms ' │ '1.54 x faster ' │
904
- │ Array_Boolean │ 16000000 │ '512ms ' │ '354ms ' │ '1.45 x faster ' │
905
- │ Array_ObjectA │ 16000000 │ '41610ms ' │ '26812ms ' │ '1.55 x faster ' │
906
- │ Array_ObjectB │ 16000000 │ '46321ms ' │ '33115ms ' │ '1.40 x faster ' │
907
- │ Array_Tuple │ 16000000 │ '1903ms ' │ '1149ms ' │ '1.66 x faster ' │
908
- Array_Vector4 │ 16000000 │ '1506ms ' │ '808ms ' │ '1.86 x faster ' │
909
- Array_Matrix4 │ 16000000 │ '6263ms ' │ '5072ms ' │ '1.23 x faster ' │
910
- └──────────────────┴────────────┴────────────┴────────────┴────────────────────────┘
886
+ ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐
887
+ │ (index) │ Iterations │ Ajv TypeCompiler │ Performance
888
+ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤
889
+ │ Number │ 16000000 │ ' 73 ms' │ ' 65 ms' │ ' 1.12 x' │
890
+ │ String │ 16000000 │ ' 281 ms' │ ' 161 ms' │ ' 1.75 x' │
891
+ │ Boolean │ 16000000 │ ' 302 ms' │ ' 151 ms' │ ' 2.00 x' │
892
+ │ Null │ 16000000 │ ' 281 ms' │ ' 149 ms' │ ' 1.89 x' │
893
+ │ RegEx │ 16000000 │ ' 689 ms' │ ' 550 ms' │ ' 1.25 x' │
894
+ │ ObjectA │ 16000000 │ ' 470 ms' │ ' 311 ms' │ ' 1.51 x' │
895
+ │ ObjectB │ 16000000 │ ' 667 ms' │ ' 556 ms' │ ' 1.20 x' │
896
+ │ Tuple │ 16000000 │ ' 362 ms' │ ' 190 ms' │ ' 1.91 x' │
897
+ │ Union │ 16000000 │ ' 387 ms' │ ' 209 ms' │ ' 1.85 x' │
898
+ │ Recursive │ 16000000 │ ' 6327 ms' │ ' 2773 ms' │ ' 2.28 x' │
899
+ │ Vector4 │ 16000000 │ ' 362 ms' │ ' 172 ms' │ ' 2.10 x' │
900
+ │ Matrix4 │ 16000000 │ ' 607 ms' │ ' 418 ms' │ ' 1.45 x' │
901
+ │ Literal_String │ 16000000 │ ' 314 ms' │ ' 151 ms' │ ' 2.08 x' │
902
+ │ Literal_Number │ 16000000 │ ' 281 ms' │ ' 147 ms' │ ' 1.91 x' │
903
+ │ Literal_Boolean │ 16000000 │ ' 289 ms' │ ' 148 ms' │ ' 1.95 x' │
904
+ │ Array_Number │ 16000000 │ ' 475 ms' │ ' 230 ms' │ ' 2.07 x' │
905
+ │ Array_String │ 16000000 │ ' 463 ms' │ ' 297 ms' │ ' 1.56 x' │
906
+ │ Array_Boolean │ 16000000 │ ' 521 ms' │ ' 348 ms' │ ' 1.50 x' │
907
+ │ Array_ObjectA │ 16000000 │ ' 43842 ms' │ ' 28375 ms' │ ' 1.55 x' │
908
+ │ Array_ObjectB │ 16000000 │ ' 45055 ms' │ ' 32882 ms' │ ' 1.37 x' │
909
+ │ Array_Tuple │ 16000000 │ ' 1424 ms' │ ' 1105 ms' │ ' 1.29 x' │
910
+ Array_Union │ 16000000 │ ' 3505 ms' │ ' 1350 ms' │ ' 2.60 x' │
911
+ Array_Recursive │ 16000000 │ ' 117087 ms' │ ' 42982 ms' │ ' 2.72 x' │
912
+ │ Array_Vector4 │ 16000000 │ ' 1500 ms' │ ' 817 ms' │ ' 1.84 x' │
913
+ │ Array_Matrix4 │ 16000000 │ ' 6126 ms' │ ' 4965 ms' │ ' 1.23 x' │
914
+ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘
911
915
  ```
912
916
 
913
- #### Compile
917
+ ### Compile
914
918
 
915
- This benchmark measures schema compilation time. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/compile.ts).
919
+ This benchmark measures compilation performance for varying types. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/benchmark/compile.ts).
916
920
 
917
921
  ```typescript
918
- ┌──────────────────┬────────────┬────────────┬────────────┬────────────────────────┐
919
- │ (index) │ Iterations │ Ajv TypeBox Measured │
920
- ├──────────────────┼────────────┼────────────┼────────────┼────────────────────────┤
921
- │ Number │ 2000 │ '386ms ' │ '7ms ' │ '55.14 x faster ' │
922
- │ String │ 2000 │ '308ms ' │ '7ms ' │ '44.00 x faster ' │
923
- │ Boolean │ 2000 │ '313ms ' │ '6ms ' │ '52.17 x faster ' │
924
- │ Null │ 2000 │ '265ms ' │ '4ms ' │ '66.25 x faster ' │
925
- │ RegEx │ 2000 │ '481ms ' │ '7ms ' │ '68.71 x faster ' │
926
- │ ObjectA │ 2000 │ '2788ms ' │ '26ms ' │ '107.23 x faster ' │
927
- │ ObjectB │ 2000 │ '2950ms ' │ '22ms ' │ '134.09 x faster ' │
928
- │ Tuple │ 2000 │ '1255ms ' │ '17ms ' │ '73.82 x faster ' │
929
- │ Union │ 2000 │ '1308ms ' │ '16ms ' │ '81.75 x faster ' │
930
- │ Vector4 │ 2000 │ '1584ms ' │ '12ms ' │ '132.00 x faster ' │
931
- │ Matrix4 │ 2000 │ '931ms ' │ '9ms ' │ '103.44 x faster ' │
932
- │ Literal_String │ 2000 │ '347ms ' │ '7ms ' │ '49.57 x faster ' │
933
- │ Literal_Number │ 2000 │ '380ms ' │ '7ms ' │ '54.29 x faster ' │
934
- │ Literal_Boolean │ 2000 │ '374ms ' │ '4ms ' │ '93.50 x faster ' │
935
- │ Array_Number │ 2000 │ '741ms ' │ '5ms ' │ '148.20 x faster ' │
936
- │ Array_String │ 2000 │ '764ms ' │ '9ms ' │ '84.89 x faster ' │
937
- │ Array_Boolean │ 2000 │ '788ms ' │ '8ms ' │ '98.50 x faster ' │
938
- │ Array_ObjectA │ 2000 │ '3545ms ' │ '25ms ' │ '141.80 x faster ' │
939
- │ Array_ObjectB │ 2000 │ '3685ms ' │ '27ms ' │ '136.48 x faster ' │
940
- │ Array_Tuple │ 2000 │ '2242ms ' │ '13ms ' │ '172.46 x faster ' │
941
- Array_Vector4 │ 2000 │ '1784ms ' │ '14ms ' │ '127.43 x faster ' │
942
- Array_Matrix4 │ 2000 │ '1622ms ' │ '10ms ' │ '162.20 x faster ' │
943
- └──────────────────┴────────────┴────────────┴────────────┴────────────────────────┘
922
+ ┌──────────────────┬────────────┬──────────────┬──────────────┬──────────────┐
923
+ │ (index) │ Iterations │ Ajv TypeCompiler │ Performance
924
+ ├──────────────────┼────────────┼──────────────┼──────────────┼──────────────┤
925
+ │ Number │ 2000 │ ' 387 ms' │ ' 7 ms' │ ' 55.29 x' │
926
+ │ String │ 2000 │ ' 340 ms' │ ' 6 ms' │ ' 56.67 x' │
927
+ │ Boolean │ 2000 │ ' 314 ms' │ ' 5 ms' │ ' 62.80 x' │
928
+ │ Null │ 2000 │ ' 259 ms' │ ' 5 ms' │ ' 51.80 x' │
929
+ │ RegEx │ 2000 │ ' 491 ms' │ ' 7 ms' │ ' 70.14 x' │
930
+ │ ObjectA │ 2000 │ ' 2850 ms' │ ' 26 ms' │ ' 109.62 x' │
931
+ │ ObjectB │ 2000 │ ' 2989 ms' │ ' 22 ms' │ ' 135.86 x' │
932
+ │ Tuple │ 2000 │ ' 1283 ms' │ ' 17 ms' │ ' 75.47 x' │
933
+ │ Union │ 2000 │ ' 1292 ms' │ ' 16 ms' │ ' 80.75 x' │
934
+ │ Vector4 │ 2000 │ ' 1588 ms' │ ' 12 ms' │ ' 132.33 x' │
935
+ │ Matrix4 │ 2000 │ ' 934 ms' │ ' 7 ms' │ ' 133.43 x' │
936
+ │ Literal_String │ 2000 │ ' 340 ms' │ ' 5 ms' │ ' 68.00 x' │
937
+ │ Literal_Number │ 2000 │ ' 402 ms' │ ' 4 ms' │ ' 100.50 x' │
938
+ │ Literal_Boolean │ 2000 │ ' 381 ms' │ ' 6 ms' │ ' 63.50 x' │
939
+ │ Array_Number │ 2000 │ ' 750 ms' │ ' 8 ms' │ ' 93.75 x' │
940
+ │ Array_String │ 2000 │ ' 760 ms' │ ' 6 ms' │ ' 126.67 x' │
941
+ │ Array_Boolean │ 2000 │ ' 796 ms' │ ' 7 ms' │ ' 113.71 x' │
942
+ │ Array_ObjectA │ 2000 │ ' 3617 ms' │ ' 26 ms' │ ' 139.12 x' │
943
+ │ Array_ObjectB │ 2000 │ ' 3823 ms' │ ' 26 ms' │ ' 147.04 x' │
944
+ │ Array_Tuple │ 2000 │ ' 2263 ms' │ ' 13 ms' │ ' 174.08 x' │
945
+ Array_Union │ 2000 │ ' 1725 ms' │ ' 16 ms' │ ' 107.81 x' │
946
+ Array_Vector4 │ 2000 │ ' 2445 ms' │ ' 14 ms' │ ' 174.64 x' │
947
+ │ Array_Matrix4 │ 2000 │ ' 1638 ms' │ ' 11 ms' │ ' 148.91 x' │
948
+ └──────────────────┴────────────┴──────────────┴──────────────┴──────────────┘
944
949
  ```
945
950
 
946
951
  <a name="Contribute"></a>
package/typebox.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export declare const Kind: unique symbol;
2
+ export declare const Hint: unique symbol;
2
3
  export declare const Modifier: unique symbol;
3
4
  export declare type TModifier = TReadonlyOptional<TSchema> | TOptional<TSchema> | TReadonly<TSchema>;
4
5
  export declare type TReadonly<T extends TSchema> = T & {
@@ -10,10 +11,6 @@ export declare type TOptional<T extends TSchema> = T & {
10
11
  export declare type TReadonlyOptional<T extends TSchema> = T & {
11
12
  [Modifier]: 'ReadonlyOptional';
12
13
  };
13
- export interface DesignType {
14
- type: string;
15
- [props: string]: any;
16
- }
17
14
  export interface SchemaOptions {
18
15
  $schema?: string;
19
16
  /** Id for this schema */
@@ -30,6 +27,7 @@ export interface SchemaOptions {
30
27
  }
31
28
  export interface TSchema extends SchemaOptions {
32
29
  [Kind]: string;
30
+ [Hint]?: string;
33
31
  [Modifier]?: string;
34
32
  params: unknown[];
35
33
  static: unknown;
@@ -109,9 +107,12 @@ export interface TIntersect<T extends TObject[] = TObject[]> extends TObject {
109
107
  static: IntersectReduce<unknown, IntersectEvaluate<T, this['params']>>;
110
108
  properties: Record<keyof IntersectReduce<unknown, IntersectEvaluate<T, this['params']>>, TSchema>;
111
109
  }
112
- declare type UnionToIntersect<U> = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never;
113
- declare type UnionLast<U> = UnionToIntersect<U extends unknown ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L : never;
114
- declare type UnionToTuple<U, L = UnionLast<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, L>>, L];
110
+ export declare type UnionToIntersect<U> = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never;
111
+ export declare type UnionLast<U> = UnionToIntersect<U extends unknown ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L : never;
112
+ export declare type UnionToTuple<U, L = UnionLast<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, L>>, L];
113
+ export declare type UnionStringLiteralToTuple<T> = T extends TUnion<infer L> ? {
114
+ [I in keyof L]: L[I] extends TLiteral<infer C> ? C : never;
115
+ } : never;
115
116
  export declare type TKeyOf<T extends TObject> = {
116
117
  [K in ObjectPropertyKeys<T>]: TLiteral<K>;
117
118
  } extends infer R ? UnionToTuple<R[keyof R]> : never;
@@ -179,10 +180,9 @@ export interface TOmit<T extends TObject, Properties extends ObjectPropertyKeys<
179
180
  export interface TPartial<T extends TObject> extends TObject {
180
181
  static: Partial<Static<T, this['params']>>;
181
182
  }
182
- export interface TPick<T extends TObject, Properties extends ObjectPropertyKeys<T>[]> extends TObject, ObjectOptions {
183
- static: Pick<Static<T, this['params']>, Properties[number]>;
184
- properties: ObjectProperties<T>;
185
- }
183
+ export declare type TPick<T extends TObject, Properties extends ObjectPropertyKeys<T>[]> = TObject<{
184
+ [K in Properties[number]]: T['properties'][K];
185
+ }>;
186
186
  export interface TPromise<T extends TSchema = TSchema> extends TSchema {
187
187
  [Kind]: 'Promise';
188
188
  static: Promise<Static<T, this['params']>>;
@@ -230,6 +230,7 @@ export interface TString extends TSchema, StringOptions<string> {
230
230
  static: string;
231
231
  type: 'string';
232
232
  }
233
+ export declare type TupleToArray<T extends TTuple<TSchema[]>> = T extends TTuple<infer R> ? R : never;
233
234
  export interface TTuple<T extends TSchema[] = TSchema[]> extends TSchema {
234
235
  [Kind]: 'Tuple';
235
236
  static: {
@@ -300,11 +301,15 @@ export declare class TypeBuilder {
300
301
  /** Creates a tuple type from this constructors parameters */
301
302
  ConstructorParameters<T extends TConstructor<any[], any>>(schema: T, options?: SchemaOptions): TConstructorParameters<T>;
302
303
  /** Creates a constructor type */
304
+ Constructor<T extends TTuple<TSchema[]>, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TConstructor<TupleToArray<T>, U>;
305
+ /** Creates a constructor type */
303
306
  Constructor<T extends TSchema[], U extends TSchema>(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor<T, U>;
304
307
  /** Creates a enum type */
305
308
  Enum<T extends Record<string, string | number>>(item: T, options?: SchemaOptions): TEnum<T>;
306
309
  /** Creates a function type */
307
- Function<T extends readonly TSchema[], U extends TSchema>(parameters: [...T], returns: U, options?: SchemaOptions): TFunction<T, U>;
310
+ Function<T extends TTuple<TSchema[]>, U extends TSchema>(parameters: T, returns: U, options?: SchemaOptions): TFunction<TupleToArray<T>, U>;
311
+ /** Creates a function type */
312
+ Function<T extends TSchema[], U extends TSchema>(parameters: [...T], returns: U, options?: SchemaOptions): TFunction<T, U>;
308
313
  /** Creates a type from this constructors instance type */
309
314
  InstanceType<T extends TConstructor<any[], any>>(schema: T, options?: SchemaOptions): TInstanceType<T>;
310
315
  /** Creates a integer type */
@@ -322,13 +327,17 @@ export declare class TypeBuilder {
322
327
  /** Creates an object type with the given properties */
323
328
  Object<T extends TProperties>(properties: T, options?: ObjectOptions): TObject<T>;
324
329
  /** Creates a new object whose properties are omitted from the given object */
325
- Omit<T extends TObject, Properties extends Array<ObjectPropertyKeys<T>>>(schema: T, keys: [...Properties], options?: ObjectOptions): TOmit<T, Properties>;
330
+ Omit<T extends TObject, K extends TUnion<TLiteral<string>[]>>(schema: T, keys: K, options?: ObjectOptions): TOmit<T, UnionStringLiteralToTuple<K>>;
331
+ /** Creates a new object whose properties are omitted from the given object */
332
+ Omit<T extends TObject, K extends ObjectPropertyKeys<T>[]>(schema: T, keys: [...K], options?: ObjectOptions): TOmit<T, K>;
326
333
  /** Creates a tuple type from this functions parameters */
327
334
  Parameters<T extends TFunction<any[], any>>(schema: T, options?: SchemaOptions): TParameters<T>;
328
335
  /** Creates an object type whose properties are all optional */
329
336
  Partial<T extends TObject>(schema: T, options?: ObjectOptions): TPartial<T>;
330
- /** Creates a new object whose properties are picked from the given object */
331
- Pick<T extends TObject, Properties extends Array<ObjectPropertyKeys<T>>>(schema: T, keys: [...Properties], options?: ObjectOptions): TPick<T, Properties>;
337
+ /** Creates a object whose properties are picked from the given object */
338
+ Pick<T extends TObject, K extends TUnion<TLiteral<string>[]>>(schema: T, keys: K, options?: ObjectOptions): TPick<T, UnionStringLiteralToTuple<K>>;
339
+ /** Creates a object whose properties are picked from the given object */
340
+ Pick<T extends TObject, K extends ObjectPropertyKeys<T>[]>(schema: T, keys: [...K], options?: ObjectOptions): TPick<T, K>;
332
341
  /** Creates a promise type. This type cannot be represented in schema. */
333
342
  Promise<T extends TSchema>(item: T, options?: SchemaOptions): TPromise<T>;
334
343
  /** Creates an object whose properties are derived from the given string literal union. */
@@ -370,4 +379,3 @@ export declare class TypeBuilder {
370
379
  }
371
380
  /** JSON Schema Type Builder with Static Type Resolution for TypeScript */
372
381
  export declare const Type: TypeBuilder;
373
- export {};
package/typebox.js CHANGED
@@ -27,11 +27,12 @@ THE SOFTWARE.
27
27
 
28
28
  ---------------------------------------------------------------------------*/
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.Type = exports.TypeBuilder = exports.Modifier = exports.Kind = void 0;
30
+ exports.Type = exports.TypeBuilder = exports.Modifier = exports.Hint = exports.Kind = void 0;
31
31
  // --------------------------------------------------------------------------
32
32
  // Symbols
33
33
  // --------------------------------------------------------------------------
34
34
  exports.Kind = Symbol.for('TypeBox.Kind');
35
+ exports.Hint = Symbol.for('TypeBox.Hint');
35
36
  exports.Modifier = Symbol.for('TypeBox.Modifier');
36
37
  // --------------------------------------------------------------------------
37
38
  // TypeBuilder
@@ -74,7 +75,16 @@ class TypeBuilder {
74
75
  }
75
76
  /** Creates a constructor type */
76
77
  Constructor(parameters, returns, options = {}) {
77
- return this.Create({ ...options, [exports.Kind]: 'Constructor', type: 'constructor', parameters, returns });
78
+ if (parameters[exports.Kind] === 'Tuple') {
79
+ const inner = parameters.items === undefined ? [] : parameters.items;
80
+ return this.Create({ ...options, [exports.Kind]: 'Constructor', type: 'constructor', parameters: inner, returns });
81
+ }
82
+ else if (globalThis.Array.isArray(parameters)) {
83
+ return this.Create({ ...options, [exports.Kind]: 'Constructor', type: 'constructor', parameters, returns });
84
+ }
85
+ else {
86
+ throw new Error('TypeBuilder.Constructor: Invalid parameters');
87
+ }
78
88
  }
79
89
  /** Creates a enum type */
80
90
  Enum(item, options = {}) {
@@ -82,11 +92,20 @@ class TypeBuilder {
82
92
  .filter((key) => isNaN(key))
83
93
  .map((key) => item[key]);
84
94
  const anyOf = values.map((value) => (typeof value === 'string' ? { [exports.Kind]: 'Literal', type: 'string', const: value } : { [exports.Kind]: 'Literal', type: 'number', const: value }));
85
- return this.Create({ ...options, [exports.Kind]: 'Union', anyOf });
95
+ return this.Create({ ...options, [exports.Kind]: 'Union', [exports.Hint]: 'Enum', anyOf });
86
96
  }
87
97
  /** Creates a function type */
88
98
  Function(parameters, returns, options = {}) {
89
- return this.Create({ ...options, [exports.Kind]: 'Function', type: 'function', parameters, returns });
99
+ if (parameters[exports.Kind] === 'Tuple') {
100
+ const inner = parameters.items === undefined ? [] : parameters.items;
101
+ return this.Create({ ...options, [exports.Kind]: 'Function', type: 'function', parameters: inner, returns });
102
+ }
103
+ else if (globalThis.Array.isArray(parameters)) {
104
+ return this.Create({ ...options, [exports.Kind]: 'Function', type: 'function', parameters, returns });
105
+ }
106
+ else {
107
+ throw new Error('TypeBuilder.Function: Invalid parameters');
108
+ }
90
109
  }
91
110
  /** Creates a type from this constructors instance type */
92
111
  InstanceType(schema, options = {}) {
@@ -128,7 +147,7 @@ class TypeBuilder {
128
147
  /** Creates a keyof type */
129
148
  KeyOf(object, options = {}) {
130
149
  const items = Object.keys(object.properties).map((key) => this.Create({ ...options, [exports.Kind]: 'Literal', type: 'string', const: key }));
131
- return this.Create({ ...options, [exports.Kind]: 'Union', anyOf: items });
150
+ return this.Create({ ...options, [exports.Kind]: 'Union', [exports.Hint]: 'KeyOf', anyOf: items });
132
151
  }
133
152
  /** Creates a literal type. */
134
153
  Literal(value, options = {}) {
@@ -160,10 +179,11 @@ class TypeBuilder {
160
179
  }
161
180
  /** Creates a new object whose properties are omitted from the given object */
162
181
  Omit(schema, keys, options = {}) {
163
- const next = { ...this.Clone(schema), ...options };
164
- next.required = next.required ? next.required.filter((key) => !keys.includes(key)) : undefined;
182
+ const select = keys[exports.Kind] === 'Union' ? keys.anyOf.map((schema) => schema.const) : keys;
183
+ const next = { ...this.Clone(schema), ...options, [exports.Hint]: 'Omit' };
184
+ next.required = next.required ? next.required.filter((key) => !select.includes(key)) : undefined;
165
185
  for (const key of Object.keys(next.properties)) {
166
- if (keys.includes(key))
186
+ if (select.includes(key))
167
187
  delete next.properties[key];
168
188
  }
169
189
  return this.Create(next);
@@ -174,7 +194,7 @@ class TypeBuilder {
174
194
  }
175
195
  /** Creates an object type whose properties are all optional */
176
196
  Partial(schema, options = {}) {
177
- const next = { ...this.Clone(schema), ...options };
197
+ const next = { ...this.Clone(schema), ...options, [exports.Hint]: 'Partial' };
178
198
  delete next.required;
179
199
  for (const key of Object.keys(next.properties)) {
180
200
  const property = next.properties[key];
@@ -196,12 +216,13 @@ class TypeBuilder {
196
216
  }
197
217
  return this.Create(next);
198
218
  }
199
- /** Creates a new object whose properties are picked from the given object */
219
+ /** Creates a object whose properties are picked from the given object */
200
220
  Pick(schema, keys, options = {}) {
201
- const next = { ...this.Clone(schema), ...options };
202
- next.required = next.required ? next.required.filter((key) => keys.includes(key)) : undefined;
221
+ const select = keys[exports.Kind] === 'Union' ? keys.anyOf.map((schema) => schema.const) : keys;
222
+ const next = { ...this.Clone(schema), ...options, [exports.Hint]: 'Pick' };
223
+ next.required = next.required ? next.required.filter((key) => select.includes(key)) : undefined;
203
224
  for (const key of Object.keys(next.properties)) {
204
- if (!keys.includes(key))
225
+ if (!select.includes(key))
205
226
  delete next.properties[key];
206
227
  }
207
228
  return this.Create(next);
@@ -216,7 +237,7 @@ class TypeBuilder {
216
237
  if (key[exports.Kind] === 'Union') {
217
238
  return this.Object(key.anyOf.reduce((acc, literal) => {
218
239
  return { ...acc, [literal.const]: value };
219
- }, {}), { ...options });
240
+ }, {}), { ...options, [exports.Hint]: 'Record' });
220
241
  }
221
242
  // otherwise return TRecord with patternProperties
222
243
  const pattern = key[exports.Kind] === 'Number' ? '^(0|[1-9][0-9]*)$' : key[exports.Kind] === 'String' && key.pattern ? key.pattern : '^.*$';
@@ -239,7 +260,7 @@ class TypeBuilder {
239
260
  /** Creates a reference schema */
240
261
  Ref(schema, options = {}) {
241
262
  if (schema.$id === undefined)
242
- throw Error('Type.Ref: Referenced schema must specify an $id');
263
+ throw Error('TypeBuilder.Ref: Referenced schema must specify an $id');
243
264
  return this.Create({ ...options, [exports.Kind]: 'Ref', $ref: schema.$id });
244
265
  }
245
266
  /** Creates a string type from a regular expression */
@@ -248,7 +269,7 @@ class TypeBuilder {
248
269
  }
249
270
  /** Creates an object type whose properties are all required */
250
271
  Required(schema, options = {}) {
251
- const next = { ...this.Clone(schema), ...options };
272
+ const next = { ...this.Clone(schema), ...options, [exports.Hint]: 'Required' };
252
273
  next.required = Object.keys(next.properties);
253
274
  for (const key of Object.keys(next.properties)) {
254
275
  const property = next.properties[key];